首页 > 其他分享 >多校A层冲刺NOIP2024模拟赛05

多校A层冲刺NOIP2024模拟赛05

时间:2024-10-11 17:11:15浏览次数:5  
标签:1000010 05 int ll 多校 pos freopen ans NOIP2024

多校A层冲刺NOIP2024模拟赛05

\(T1\) A. 好数(number) \(100pts/100pts\)

  • 枚举两数之和,开个桶维护即可。

    点击查看代码
    int a[5010];
    unordered_map<int,bool>s;
    int main()
    {
    	freopen("number.in","r",stdin);
    	freopen("number.out","w",stdout);
    	int n,ans=0,i,j;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		for(j=i;j>=1;j--)
    		{
    			if(s.find(a[i]-a[j])!=s.end())
    			{
    				ans++;
    				break;
    			}
    		}
    		for(j=i;j>=1;j--)
    		{
    			s[a[i]+a[j]]=1;
    		}
    	}
    	cout<<ans<<endl;
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T2\) B. SOS字符串(sos) \(20pts/20pts\)

  • 观察到仅需统计 SOS出现次数 \(\ge 3\) 。正难则反,考虑用总方案数减去 SOS出现次数 \(<3\) 的方案数。

  • 总方案数显然为 \(26^{n}\) 。

  • 部分分

    • \(20pts\) :打表加手摸。
    点击查看代码
    const ll p=1000000007;
    int ans=0;
    string s;
    vector<char>e;
    unordered_map<string,bool>vis;
    void dfs1(int pos)
    {
    	if(pos==e.size())
    	{
    		if(vis.find(s)==vis.end())
    		{
    			ans++;
    			ans-=(ans>=p);
    			vis[s]=1;
    		}
    	}
    	else
    	{
    		if(e[pos]=='*')
    		{
    			for(char i='A';i<='Z';i++)
    			{
    				s[pos]=i;
    				dfs1(pos+1);
    			}
    		}
    		else
    		{
    			s[pos]='S';
    			s[pos+1]='O';
    			s[pos+2]='S';
    			dfs1(pos+3);
    		}
    	}
    }
    void dfs2(int pos,int n,int sum)
    {
    	if(pos<=n+1)
    	{
    		if(pos==n+1)
    		{
    			if(sum>=3)
    			{
    				dfs1(0);
    			}
    		}
    		else
    		{
    			e.push_back('*');
    			dfs2(pos+1,n,sum);
    			e.pop_back();
    			e.push_back('S');
    			e.push_back('O');
    			e.push_back('S');
    			dfs2(pos+3,n,sum+1);
    			e.pop_back();
    			e.pop_back();
    			e.pop_back();
    		}
    	}
    }
    int main()
    {
    	freopen("sos.in","r",stdin);
    	freopen("sos.out","w",stdout);
    	int n;
    	cin>>n;
    	if(n==13)
    	{
    		cout<<15973496<<endl;
    	}
    	else
    	{
    		s.resize(n+1);
    		dfs2(1,n,0);
    		cout<<ans<<endl;    
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 正解

    • S,O 和其他 \(24\) 个字符分开考虑。
    • 设 \(f_{i,j,k}\) 表示前 \(i\) 个字符中已经出现了 \(j\) 个 SOS ,现在匹配到 SOS 的第 \(k\) 个字符(因为有 SOSOS 只算一次的要求,所以把 \(k=3\) 同样看做 \(k=0\) )的方案数。状态转移方程为 \(\begin{cases} f_{i,j,0}=25(f_{i-1,j,0}+f_{i-1,j,2})+24f_{i-1,j,1}+f_{i-1,j-1,2} \\ f_{i,j,1}=f_{i-1,j,0}+f_{i-1,j,1} \\ f_{i,j,2}=f_{i-1,j,1} \end{cases}\) ,边界为 \(f_{0,0,0}=1\) 。
    • 最终,有 \(26^{n}-\sum\limits_{j=0}^{2}\sum\limits_{k=0}^{2}f_{n,j,k}\) 即为所求。
    点击查看代码
    const ll p=1000000007;
    ll f[2][5][5];
    ll qpow(ll a,ll b,ll p)
    {
    	ll ans=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=ans*a%p;
    		}
    		b>>=1;
    		a=a*a%p;
    	}
    	return ans;
    }
    int main()
    {
    	freopen("sos.in","r",stdin);
    	freopen("sos.out","w",stdout);
    	ll n,ans,i,j;
    	cin>>n;
    	ans=qpow(26,n,p);
    	f[0][0][0]=1;
    	for(i=1;i<=n;i++)
    	{
    		for(j=0;j<=2;j++)
    		{
    			f[i&1][j][0]=((f[(i-1)&1][j][0]+f[(i-1)&1][j][2])%p*25%p+f[(i-1)&1][j][1]*24%p)%p;
    			if(j-1>=0)
    			{
    				f[i&1][j][0]=(f[i&1][j][0]+f[(i-1)&1][j-1][2])%p;
    			}
    			f[i&1][j][1]=(f[(i-1)&1][j][0]+f[(i-1)&1][j][1])%p;
    			f[i&1][j][2]=f[(i-1)&1][j][1];
    		}
    	}
    	for(j=0;j<=2;j++)
    	{
    		ans=(ans-f[n&1][j][0]+p)%p;
    		ans=(ans-f[n&1][j][1]+p)%p;
    		ans=(ans-f[n&1][j][2]+p)%p;
    	}
    	cout<<ans<<endl;
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T3\) C. 集训营的气球(balloon) \(50pts/50pts\)

  • 观察到 \(c \le 20\) 。正难则反,考虑用总方案数减去顾客中有 \(<c\) 个人购买一血气球的方案数。

  • 总方案数显然为 \(\prod\limits_{i=1}^{n}(a_{i}+b_{i})\) 。

  • 部分分

    • \(50pts\)
      • 测试点 \(3 \sim 5\)

        • 设 \(f_{i,j}\) 表示前 \(i\) 个人中共有 \(j\) 个人买一血气球的方案数,状态转移方程为 \(f_{i,j}=a_{i}f_{i-1,j-1}+b_{i}f_{i-1,j}\) ,边界为 \(f_{0,0}=1\) 。
        • 最终有 \(\prod\limits_{i=1}^{n}(a_{i}+b_{i})-\sum\limits_{i=0}^{\min(c-1,n)}f_{n,i}\) 即为所求。
        • 时间复杂度为 \(O(nqc)\) 。
      • 测试点 \(1 \sim 2\)

        • 因为 \(c=1\) ,所以每个人都只能买普通气球。故 \(\prod\limits_{i=1}^{n}(a_{i}+b_{i})-\prod\limits_{i=1}^{n}b_{i}\) 即为所求。
        • 时间复杂度为 \(O((n+q)\log p)\) 。
        点击查看代码
        const ll p=1000000007;
        ll a[1000010],b[1000010],inv[1000010],invb[1000010],f[2][30];
        ll qpow(ll a,ll b,ll p)
        {
        	ll ans=1;
        	while(b)
        	{
        		if(b&1)
        		{
        			ans=ans*a%p;
        		}
        		b>>=1;
        		a=a*a%p;
        	}
        	return ans;
        }
        ll qadd(ll a,ll b,ll p)
        {
        	return a+b>=p?a+b-p:a+b;
        }
        int main()
        {
        	freopen("balloon.in","r",stdin);
        	freopen("balloon.out","w",stdout);
        	ll n,q,c,mul=1,sum,pos,i,j,k;
        	scanf("%lld%lld",&n,&c);
        	c--;
        	for(i=1;i<=n;i++)
        	{
        		scanf("%lld",&a[i]);
        	}
        	for(i=1;i<=n;i++)
        	{
        		scanf("%lld",&b[i]);
        		inv[i]=qpow(a[i]+b[i],p-2,p);
        		mul=mul*(a[i]+b[i])%p;
        	}
        	scanf("%lld",&q);
        	if(c==0)
        	{
        		sum=1;
        		for(i=1;i<=n;i++)
        		{
        			invb[i]=qpow(b[i],p-2,p);
        			sum=sum*b[i]%p;
        		}
        		for(k=1;k<=q;k++)
        		{
        			scanf("%lld",&pos);
        			mul=mul*inv[pos]%p;
        			sum=sum*invb[pos]%p;
        			scanf("%lld%lld",&a[pos],&b[pos]);
        			invb[pos]=qpow(b[pos],p-2,p);
        			inv[pos]=qpow(a[pos]+b[pos],p-2,p);
        			mul=mul*(a[pos]+b[pos])%p;
        			sum=sum*b[pos]%p;
        			printf("%lld\n",(mul-sum+p)%p);
        		}
        	}
        	else
        	{
        		for(k=1;k<=q;k++)
        		{
        			scanf("%lld",&pos);
        			mul=mul*inv[pos]%p;
        			scanf("%lld%lld",&a[pos],&b[pos]);
        			inv[pos]=qpow(a[pos]+b[pos],p-2,p);
        			mul=mul*(a[pos]+b[pos])%p;
        			sum=0;
        			memset(f,0,sizeof(f));
        			f[0][0]=1;
        			for(i=1;i<=n;i++)
        			{
        				for(j=0;j<=min(i,c);j++)
        				{
        					f[i&1][j]=f[(i-1)&1][j]*b[i]%p;
        					if(j-1>=0)
        					{
        						f[i&1][j]=qadd(f[i&1][j],f[(i-1)&1][j-1]*a[i]%p,p);
        					}
        				}
        			}
        			for(i=0;i<=min(n,c);i++)
        			{
        				sum=qadd(sum,f[n&1][i],p);
        			}
        			printf("%lld\n",(mul-sum+p)%p);
        		}
        	}
        	fclose(stdin);
        	fclose(stdout);
        	return 0;
        }
        
    • \(80pts\)
      • \(\{ f \}\) 的转移本质是一个背包求方案数的转移过程,使用动态开点线段树或 查理线段树 (否则空间开不下)优化合并背包的过程即可。

      • 时间复杂度为 \(O((n+q)c^{2})\) 。

        点击查看动态开点线段树代码
        const ll p=1000000007;
        int a[1000010],b[1000010];
        struct SMT
        {
        	int root,rt_sum;
        	struct SegmentTree
        	{
        		int ls,rs,f[25],mul;
        	}tree[2000010];
        	#define lson(rt) (tree[rt].ls)
        	#define rson(rt) (tree[rt].rs)
        	void pushup(int rt,int c)
        	{
        		memset(tree[rt].f,0,sizeof(tree[rt].f));
        		for(int i=0;i<=c;i++)
        		{
        			for(int j=0;i+j<=c;j++)
        			{
        				tree[rt].f[i+j]=(tree[rt].f[i+j]+1ll*tree[lson(rt)].f[i]*tree[rson(rt)].f[j]%p)%p;
        			}
        		}
        		tree[rt].mul=1ll*tree[lson(rt)].mul*tree[rson(rt)].mul%p;
        	}
        	int build_rt()
        	{
        		rt_sum++;
        		return rt_sum;
        	}
        	void build(int &rt,int l,int r,int c)
        	{
        		rt=build_rt();
        		if(l==r)
        		{
        			tree[rt].f[0]=b[l];
        			tree[rt].f[1]=a[l];
        			tree[rt].mul=a[l]+b[l];
        			return;
        		}
        		int mid=(l+r)/2;
        		build(lson(rt),l,mid,c);
        		build(rson(rt),mid+1,r,c);
        		pushup(rt,c);
        	}
        	void update(int rt,int l,int r,int pos,int c)
        	{
        		if(l==r)
        		{
        			tree[rt].f[0]=b[l];
        			tree[rt].f[1]=a[l];
        			tree[rt].mul=a[l]+b[l];
        			return;
        		}
        		int mid=(l+r)/2;
        		if(pos<=mid)
        		{
        			update(lson(rt),l,mid,pos,c);
        		}
        		else
        		{
        			update(rson(rt),mid+1,r,pos,c);
        		}
        		pushup(rt,c);
        	}
        }T; 
        int main()
        {
        	freopen("balloon.in","r",stdin);
        	freopen("balloon.out","w",stdout);
        	int n,c,q,pos,ans,i,j;
        	scanf("%d%d",&n,&c);
        	c--;
        	for(i=1;i<=n;i++)
        	{
        		scanf("%d",&a[i]);
        	}
        	for(i=1;i<=n;i++)
        	{
        		scanf("%d",&b[i]);
        	}
        	T.build(T.root,1,n,c);
        	scanf("%d",&q);
        	for(j=1;j<=q;j++)
        	{
        		scanf("%d",&pos);
        		scanf("%d%d",&a[pos],&b[pos]);
        		T.update(T.root,1,n,pos,c);
        		ans=T.tree[T.root].mul;
        		for(i=0;i<=c;i++)
        		{
        			ans=(ans-T.tree[T.root].f[i]+p)%p;
        		}
        		printf("%d\n",ans);
        	}
        	fclose(stdin);
        	fclose(stdout);
        	return 0;
        }
        
        点击查看查理线段树代码
        const ll p=1000000007;
        int a[1000010],b[1000010];
        struct SMT
        {
        	struct SegmentTree
        	{
        		int f[25],mul;
        	}tree[2000010];
        	#define lson(rt) (mid<<1)
        	#define rson(rt) (mid<<1|1)
        	void pushup(int rt,int c,int l,int r)
        	{
        		int mid=(l+r)/2;
        		memset(tree[rt].f,0,sizeof(tree[rt].f));
        		for(int i=0;i<=c;i++)
        		{
        			for(int j=0;i+j<=c;j++)
        			{
        				tree[rt].f[i+j]=(tree[rt].f[i+j]+1ll*tree[lson(rt)].f[i]*tree[rson(rt)].f[j]%p)%p;
        			}
        		}
        		tree[rt].mul=1ll*tree[lson(rt)].mul*tree[rson(rt)].mul%p;
        	}
        	void build(int rt,int l,int r,int c)
        	{
        		if(l==r)
        		{
        			tree[rt].f[0]=b[l];
        			tree[rt].f[1]=a[l];
        			tree[rt].mul=a[l]+b[l];
        			return;
        		}
        		int mid=(l+r)/2;
        		build(lson(rt),l,mid,c);
        		build(rson(rt),mid+1,r,c);
        		pushup(rt,c,l,r);
        	}
        	void update(int rt,int l,int r,int pos,int c)
        	{
        		if(l==r)
        		{
        			tree[rt].f[0]=b[l];
        			tree[rt].f[1]=a[l];
        			tree[rt].mul=a[l]+b[l];
        			return;
        		}
        		int mid=(l+r)/2;
        		if(pos<=mid)
        		{
        			update(lson(rt),l,mid,pos,c);
        		}
        		else
        		{
        			update(rson(rt),mid+1,r,pos,c);
        		}
        		pushup(rt,c,l,r);
        	}
        }T; 
        int main()
        {
        	freopen("balloon.in","r",stdin);
        	freopen("balloon.out","w",stdout);
        	int n,c,q,pos,ans,i,j;
        	scanf("%d%d",&n,&c);
        	c--;
        	for(i=1;i<=n;i++)
        	{
        		scanf("%d",&a[i]);
        	}
        	for(i=1;i<=n;i++)
        	{
        		scanf("%d",&b[i]);
        	}
        	T.build(1,1,n,c);
        	scanf("%d",&q);
        	for(j=1;j<=q;j++)
        	{
        		scanf("%d",&pos);
        		scanf("%d%d",&a[pos],&b[pos]);
        		T.update(1,1,n,pos,c);
        		ans=T.tree[1].mul;
        		for(i=0;i<=c;i++)
        		{
        			ans=(ans-T.tree[1].f[i]+p)%p;
        		}
        		printf("%d\n",ans);
        	}
        	fclose(stdin);
        	fclose(stdout);
        	return 0;
        }
        
  • 正解

    • 对于修改操作,使用回退背包维护即可,做法详见 luogu P4141 消失之物
      • luogu P4141 消失之物 为例,滚动数组下倒序插入背包预处理代码如下。
        for(j=m;j>=w[i];j--)
        {
        	f[j]+=f[j-w[i]];
        }
        
      • 在少了第 \(i\) 件物品的情况下,等价于少了一轮上面的转移。需要正序撤销背包的影响,画个图会更好理解。
        memcpy(g,s,sizeof(f));//新开数组的写法比较通用
        for(j=w[i];j<=m;j++)
        {
        	g[j]-=g[j-w[i]];
        }
        
    点击查看代码
    const ll p=1000000007;
    ll a[1000010],b[1000010],f[30],g[30];
    ll qpow(ll a,ll b,ll p)
    {
    	ll ans=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=ans*a%p;
    		}
    		b>>=1;
    		a=a*a%p;
    	}
    	return ans;
    }
    int main()
    {
    	freopen("balloon.in","r",stdin);
    	freopen("balloon.out","w",stdout);
    	ll n,q,c,mul=1,sum,inv,pos,i,j,k;
    	scanf("%lld%lld",&n,&c);
    	c--;
    	for(i=1;i<=n;i++)
    	{
    		scanf("%lld",&a[i]);
    	}
    	f[0]=1;
    	for(i=1;i<=n;i++)
    	{
    		scanf("%lld",&b[i]);
    		mul=mul*(a[i]+b[i])%p;
    		for(j=c;j>=0;j--)
    		{
    			f[j]=f[j]*b[i]%p;
    			if(j-1>=0)
    			{
    				f[j]=(f[j]+f[j-1]*a[i]%p)%p;
    			}
    		}
    	}
    	scanf("%lld",&q);
    	for(k=1;k<=q;k++)
    	{
    		scanf("%lld",&pos);
    		mul=mul*qpow(a[pos]+b[pos],p-2,p)%p;
    		inv=qpow(b[pos],p-2,p);
    		g[0]=1;
    		for(j=0;j<=c;j++)
    		{
    			if(j-1>=0)
    			{
    				g[j]=((f[j]-g[j-1]*a[pos]+p)%p)*inv%p;
    			}
    			else
    			{   
    				g[j]=f[j]*inv%p;
    			}
    		}
    		for(j=0;j<=c;j++)
    		{
    			f[j]=g[j];
    		}
    		scanf("%lld%lld",&a[pos],&b[pos]);
    		mul=mul*(a[pos]+b[pos])%p;
    		for(j=c;j>=0;j--)
    		{
    			f[j]=f[j]*b[pos]%p;
    			if(j-1>=0)
    			{
    				f[j]=(f[j]+f[j-1]*a[pos]%p)%p;
    			}
    		}
    		sum=0;
    		for(i=0;i<=c;i++)
    		{
    			sum=(sum+f[i])%p;
    		}
    		printf("%lld\n",(mul-sum+p)%p);
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T4\) D. 连通子树与树的重心(tree) \(0pts/0pts\)

总结

  • \(T2\)
    • 一直在想通配符 *(A-Z) 的贡献怎么考虑,没有想到把 S,O, 和其他 \(24\) 个字符分开考虑统计贡献。
    • 没想到正难则反了,挺好。
  • \(T3\)
    • Alt 加方向键的拖拽有点害人,检查 \(T3\) 代码时再次发现把
      scanf("%lld%lld",&n,&c);
      c--;
      
      粘成了
      c--;
      scanf("%lld%lld",&n,&c);
      
    • 想到正难则反了,挺好。
    • 感觉之前学了假的背包,没想到能够转化为背包合并。
  • \(T4\) 全程没读明白题,没搞懂为啥样例 \(3\) 的答案是 \(6\) ,手摸出来结果是 \(5=\{ \{ 2 \}, \{ 1,2,3 \} , \{ 1,2,4 \} , \{ 1,2,5 \} , \{ 1,2,4,5 \} \}\) 。
    • 看到原版题面后发现子树包括原树。

后记

  • \(T2\) 初始数据仅有 \(10 \%\) 的数据符合 \(n \le 12\) ,与数据范围中“对于 \(20 \%\) 的数据,\(n \le 12\)” 不符。赛后更改了 \(n \le 12\) 的数据进行重测。

标签:1000010,05,int,ll,多校,pos,freopen,ans,NOIP2024
From: https://www.cnblogs.com/The-Shadow-Dragon/p/18458897

相关文章

  • 多校 A 层冲刺 NOIP2024 模拟赛 05
    多校A层冲刺NOIP2024模拟赛05T1好数(number)签到题首先\(O(nV)\)的背包暴力是显然的,注意到本题只需要合法性,状态只有\(0/1\),上\(bitset\)优化转移即可。时间复杂度\(O(\frac{nV}{w})\)。T2SOS字符串(sos)签到题计数题难点在不重不漏,而本题则主要是不重。考虑一种好的......
  • ETA6005Q3Q 2.5A带动态路径管理的单节锂电开关型充电器
    2.5A带动态路径管理的单节锂电开关型充电器  ETA6005是一款充电电流达2.5A的单节锂电开关型充电。其集成了动态路径管理功能,内部路径的开关内阻仅50mohm,允许系统在没有电池的情况下,仍然可以在适配器存在是维持系统正常工作。ETA6005有特有的2级充电设定可通过引脚的0,1来设......
  • 学习011-08-05 Boolean Properties(布尔属性)
    BooleanProperties(布尔属性)InXAF,thefollowingcontrolscandisplayBooleanandNullableBooleanproperties:在XAF中,以下控件可以显示布尔和Nullable布尔属性:Acheckboxcontrol(default).复选框控件(默认)。Adrop-downcontrolthatdisplaysBooleanvaluesa......
  • PAT甲级1005 Spell It Right
    介绍Givenanon-negativeintegerN,yourtaskistocomputethesumofallthedigitsofN,andoutputeverydigitofthesuminEnglish.InputSpecification:Eachinputfilecontainsonetestcase.EachcaseoccupiesonelinewhichcontainsanN(≤10的1......
  • 2023杭电多校4
    2023杭电多校4a-bProblem题目大意:每个物品都有a,ba,ba,b两个值,......
  • 10.10日noip多校联考总结
    10.10日noip多校联考总结T1感觉就是个dij再多记录一个换乘次数然后就像普通dij一样跑就行了。但是必须得将换乘次数放进dis数组中当成一个状态记录下来,不能只记录在堆中,不然做法会假。T2发现m=0的部分分就是用一个数据结构维护区间最大子段和。m=1/2就是同时维护一个最大值......
  • [AGC054D] (ox) 题解
    感觉看到交换就应该要想到逆序对。思路一个前置小知识,我们把一个排列用相邻交换复原的最小次数是逆序对数量。考虑没有ox的情况。我们顺着扫一遍字符串。把左括号正一,右括号看作负一,当前缀和小于零以后,我们把后面最近的左括号提过来,这样可以构造出交换次数最少的合法括号串......
  • 2024牛客暑假多校第三场 - A. Bridging the Gap 2
    思路全在注释里了:#include<cstdio>#include<cmath>#include<algorithm>usingnamespacestd;constintN=5e5+5;intn,l,r,a[N];boolSolve(){ //打工次数:一个人能将其他人运过去的次数=一个人能过去以后能往返的次数 scanf("%d%d%d",&n,&l,&r); intmin_go=c......
  • 20222305 2024-2025-1 《网络与系统攻防技术》实验一实验报告
    网络攻防实验报告姓名:田青学号:20222305实验日期:2024/09/29—2024/10/09实验名称:缓冲区溢出和shellcode指导教师:王志强1.实验内容本周学习内容总结:学习了系统安全(缓冲区溢出是重点)主要内容:漏洞简介:定义以及安全漏洞。BOF(缓冲区溢出):直接原因-没有严格的内存越界检查......
  • [赛记] 多校A层冲刺NOIP2024模拟赛04
    这场ACCODERS忘交,结果最后想起来匆匆只交了T1,然后文件名还没改,所以爆零了。。。02表示法100pts高精度,不说了;点击查看代码#include<iostream>#include<cstdio>#include<string>#include<cmath>#include<algorithm>usingnamespacestd;stringp[605];stringan......