暑假集训CSP提高模拟6

暑假集训CSP提高模拟6

组题人: @KafuuChinocpp | @Chen_jr

\(T1\) P162. 花间叔祖 \(100pts\)

  • 原题: [ARC148A] mod M

  • 不难发现,最终答案只有 \(1\)\(2\) 两种取值。

  • \(\exists p \ge 2,a_{1} \equiv a_{2} \equiv a_{3} \dots \equiv a_{n} \pmod {p}\) ,则最终答案为 \(1\) ;否则,答案为 \(2\)

  • 又因为 \(x \equiv y \pmod p\) 当且仅当 \(p|(x-y)\) 。进而得到 \(\begin{cases} p|(a_{2}-a_{1}) \\ p|(a_{3}-a_{2}) \\ \dots \\ p|(a_{n}-a_{n-1}) \end{cases}\) ,取 \(p=\gcd(a_{2}-a_{1},a_{3}-a_{2}, \dots ,a_{n}-a_{n-1})\) 即可。

    点击查看代码
    int a[200010];
    int gcd(int a,int b)
    {
        return b?gcd(b,a%b):a;
    }
    int main()
    {
        int n,d=0,i;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        sort(a+1,a+1+n);
        for(i=2;i<=n;i++)
        {
            d=gcd(d,a[i]-a[i-1]);
        }
        cout<<((d==1)?2:1)<<endl;
        return 0;
    }
    
  • 赛时写得比较唐,取 \(a_{2}-a _{1},a_{3}-a_{2}, \dots ,a_{n}-a_{n-1}\) 的最小非 \(1\) 公因数,质因数分解后开桶记录质因子出现次数。因为 \(1 \times 10^{9}\) 内质因子数量不超过 \(10\) 所以时空复杂度可以接受。

    点击查看代码
    int a[200010],prime[200010],c[200010],len=0;
    void divede(int x)
    {
    	for(int i=2;i*i<=x;i++)
    	{
    		if(x%i==0)
    		{
    			len++;
    			prime[len]=i;
    			c[len]++;
    			while(x%i==0)
    			{
    				x/=i;
    			}
    		}
    	}
    	if(x>1)
    	{
    		len++;
    		prime[len]=x;
    		c[len]++;
    	}
    }
    int main()
    {
    	int n,flag=0,x,pos=0,i,j;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=2;i<=n;i++)
    	{
    		if(a[i]!=a[i-1])
    		{
    			pos=i;
    			break;
    		}
    	}
    	if(pos!=0)
    	{
    		divede(abs(a[pos]-a[pos-1]));
    		for(i=2;i<=n;i++)
    		{
    			if(i!=pos)
    			{
    				x=abs(a[i]-a[i-1]);
    				for(j=1;j<=len;j++)
    				{
    					c[j]+=(x%prime[j]==0);
    				}
    			}
    		}
    		for(i=1;i<=len;i++)
    		{
    			flag|=(c[i]==n-1);
    		}
    	}
    	else
    	{
    		flag=1;
    	}
    	cout<<((flag==0)?2:1)<<endl;
    	return 0;
    }
    

\(T2\) P150. 合并r \(10pts\)

  • 原题: [ARC107D] Number of Multisets

  • 部分分

    • \(10pts\) :当 \(n=1\) 时,输出 1
  • \(f_{i,j}\) 表示填到第 \(i\) 个数时元素和为 \(j\) 的方案数,此时可以选择填 \(1\) 或让先前填的数都除以 \(2\) ,故状态转移方程为 \(f_{i,j}=f_{i-1,j-1}+f_{i,2j}\) ,边界为 \(f_{0,0}=1\)

    点击查看代码
    const ll p=998244353;
    ll f[5010][5010];
    int main()
    {
    	ll n,k,i,j;
    	cin>>n>>k;
    	f[0][0]=1;
    	for(i=1;i<=n;i++)
    	{
    		for(j=i;j>=1;j--)
    		{
    			f[i][j]=f[i-1][j-1];
    			if(2*j<=i)
    			{
    				f[i][j]=(f[i][j]+f[i][2*j])%p;
    			}
    		}
    	}
    	cout<<f[n][k]<<endl;
    	return 0;
    }
    

\(T3\) P139. 回收波特 \(47pts\)

  • 原题: [ARC149D] Simultaneous Sugoroku

  • 部分分

    • \(47pts\)\(O(nm)\) 暴力枚举即可。

      点击查看代码
      ll x[300010],ans[300010];
      int main()
      {
      	ll n,m,d,i,j;
      	scanf("%lld%lld",&n,&m);
      	for(i=1;i<=n;i++)
      	{
      		scanf("%lld",&x[i]);
      	}
      	for(i=1;i<=m;i++)
      	{
      		scanf("%lld",&d);
      		for(j=1;j<=n;j++)
      		{
      			if(ans[j]==0)
      			{
      				x[j]+=((x[j]>0)?-d:d);
      				if(x[j]==0)
      				{
      					ans[j]=i;
      				}
      			}
      		}
      	}
      	for(i=1;i<=n;i++)
      	{
      		if(ans[i]!=0)
      		{
      			printf("Yes %lld\n",ans[i]);
      		}
      		else
      		{
      			printf("No %lld\n",x[i]);
      		}
      	}
      	return 0;
      }
      
  • 正解

    • 充分发扬人类智慧,我们发现任一时刻,若两个点关于原点对称则它们此后的位置一定一直关于原点对称。
    • 波特是动的,但原点是不动的。考虑反过来,让原点动,波特不动。
    • 考虑维护值域,预处理出所有点的答案,最后进行询问。
    • 每次原点移动后将区间分成在原点左侧、在原点上、在原点右侧三个部分,类似启发式合并,将小区间对称到大区间里,建边方便继承答案,然后将小区间删掉。由于每个点至多被删除 \(1\) 次所以处理操作时间复杂度为 \(O(V)\)
    • 最后再 \(DFS\) 一遍处理出没删掉的点的答案。
    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[2000010];
    int head[2000010],x[2000010],dfn[2000010],pos[2000010],vis[2000010],cnt=0;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void dfs(int x)
    {
    	vis[x]=1;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(vis[e[i].to]==0)
    		{
    			if(dfn[x]==0)
    			{
    				pos[e[i].to]=-pos[x];//不能停,坐标取反
    			}
    			else
    			{
    				dfn[e[i].to]=dfn[x];//能停,直接继承
    			}
    			dfs(e[i].to);
    		}
    	}
    }
    int main()
    {
    	int n,m,l=1,r=1000000,d,mid=0,dir=0,i,j;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>x[i];
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>d;
    		mid+=(dir==0)?d:-d;
    		if(l<=mid&&mid<=r)
    		{   
    			dfn[mid]=i;
    			if(mid-1-l+1<=r-(mid+1)+1)
    			{
    				for(j=l;j<=mid-1;j++)
    				{
    					add(mid*2-j,j);
    				}
    				l=mid+1;
    				dir=0;
    			}
    			else
    			{
    				for(j=mid+1;j<=r;j++)
    				{
    					add(mid*2-j,j);
    				}
    				r=mid-1;
    				dir=1;
    			}
    		}
    		else
    		{
    			dir=(mid>r);
    		}
    	}
    	for(i=l;i<=r;i++)
    	{
    		if(dfn[i]==0)
    		{
    			pos[i]=i-mid;
    		}
    		dfs(i);
    	}
    	for(i=1;i<=l-1;i++)
    	{
    		if(dfn[i]!=0)
    		{
    			dfs(i);
    		}
    	}
    	for(i=r+1;i<=1000000;i++)
    	{
    		if(dfn[i]!=0)
    		{
    			dfs(i);
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		if(dfn[x[i]]==0)
    		{
    			cout<<"No "<<pos[x[i]]<<endl;
    		}
    		else
    		{
    			cout<<"Yes "<<dfn[x[i]]<<endl;
    		}
    	}
    	return 0;
    }
    

\(T4\) T3342. 斗篷 \(0pts\)

  • 原题: luogu P6949 [ICPC2018 WF] Triangles

  • 发现数据的输入格式是有一定限制的。

  • 只考虑统计形如△的三角形,然后将整个图翻转过来求解。

  • 预处理出 \(ld_{x,y},rd_{x,y},l_{x,y}\) 分别表示 \((x,y)\) 向左上/右上/左边所能延伸的最长距离。

  • 枚举 \((x,y)\) 作为三角形的右下角,则能进行转移的三角形长度 \(i \in [1,\min(l_{x,y},ld_{x,y})]\) 若满足 \(rd_{x-i,y} \ge i\) 则对答案产生 \(1\) 的贡献。

    • 自觉忽略 --- 的影响。
  • 观察到 \(\forall i \in [1,rd_{x,y}],rd_{x+i-i,y}=rd_{x,y} \ge i\) ,即 \((x,y)\) 会对 \((k,y)(k \in [x+1,x+rd_{x,y}])\) 产生 \(1\) 的贡献,进行差分,扫描线维护一下即可。具体地,在 \((x,y)\) 的位置进行加一,在 \((x+rd_{x,y})\) 进行减一, vector 存一下要减哪些数的位置。

    点击查看代码
    struct BIT
    {
    	ll c[12010];
    	void init()
    	{
    		memset(c,0,sizeof(c));
    	}
    	ll lowbit(ll x)
    	{
    		return (x&(-x));
    	}
    	void update(ll n,ll x,ll val)
    	{
    		for(ll i=x;i<=n;i+=lowbit(i))
    		{
    			c[i]+=val;
    		}
    	}
    	ll getsum(ll x)
    	{
    		ll ans=0;
    		for(ll i=x;i>=1;i-=lowbit(i))
    		{
    			ans+=c[i];
    		}
    		return ans;
    	}
    }T;
    short ld[6010][12010],rd[6010][12010],l[6010][12010];
    char s1[6010][12010],s2[6010][12010];
    vector<ll>d[12010];
    string t;
    ll ask(char s[6010][12010],ll n,ll m)
    {
    	memset(ld,0,sizeof(ld));
    	memset(rd,0,sizeof(rd));
    	memset(l,0,sizeof(l));
    	ll ans=0,i,j,k;
    	for(i=1;i<=n;i+=2)
    	{
    		T.init();
    		for(j=(s[i][1]==' '?3:1),k=1;j<=m;j+=4,k++)
    		{
    			ld[i][j]=(s[i-1][j-1]!=' '&&i-2>=0&&j-2>=0)?ld[i-2][j-2]+1:0;
    			rd[i][j]=(s[i-1][j+1]!=' '&&i-2>=0&&j+2<=m)?rd[i-2][j+2]+1:0;
    			l[i][j]=(s[i][j-1]!=' '&&j-4>=0)?l[i][j-4]+1:0;
    			ans+=T.getsum(k)-T.getsum(k-min(ld[i][j],l[i][j])-1);
    			T.update(m,k,1);
    			d[k+rd[i][j]].push_back(k);
    			while(d[k].empty()==0)
    			{
    				T.update(m,d[k].back(),-1);
    				d[k].pop_back();
    			}
    		}
    	}
    	return ans;
    }
    int main()
    {
    	ll r,c,n,m,i,j;
    	cin>>r>>c;
    	n=2*r-1;
    	m=2*c-1;
    	for(i=0;i<=n;i++)
    	{
    		getline(cin,t);
    		while(t.size()<m)
    		{
    			t+=' ';
    		}
    		s1[i][0]=' ';
    		for(j=0;j<=m-1;j++)
    		{
    			s1[i][j+1]=t[j];
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		s2[i][0]=' ';
    		for(j=1;j<=m;j++)
    		{
            	s2[i][j]=s1[n-i+1][m-j+1];
    		}
    	}
    	cout<<ask(s1,n,m)+ask(s2,n,m)<<endl;
    	return 0;
    }
    

总结

  • \(T1\) 没想到直接取 \(\gcd\) ,导致要质因数分解。

后记

  • 题解名称为 hustDelov.md

posted @ 2024-07-24 06:36  hzoi_Shadow  阅读(124)  评论(6编辑  收藏  举报
扩大
缩小