Codeforces Round #705 (Div. 2)

Contest Link

D - GCD of an Array

给定一个序列,每次将某个位置乘上 (x) 并询问当前的 (gcd) ,对 (1e9+7) 取模。(n,qleq 2e5) .


明显是拆因子+线段树……但是可以用 unordered_map/cy

//Author: RingweEH
unordered_map<int,map<int,int> >mp,cnt;

void Work( int pos,int x )
{
	for ( int i=2; i<=sqrt(x); i++ )
		while ( x%i==0 )
		{
			mp[pos][i]++; cnt[i][mp[pos][i]]++;
			if ( cnt[i][mp[pos][i]]==n ) ans=ans*i%Mod;
			x/=i;
		}
	if ( x>1 )
	{
		mp[pos][x]++; cnt[x][mp[pos][x]]++;
		if ( cnt[x][mp[pos][x]]==n ) ans=ans*x%Mod;
	}
}

int main()
{
	n=read(); q=read();
	for ( int i=1 ;i<=n; i++ ) a[i]=read(),Work(i,a[i]);
	for ( int pos,x; q; q-- )
		pos=read(),x=read(),Work(pos,x),printf("%lld
",ans );

	return 0;
}

E - Enormous XOR

定义 (g(l,r)=xoplus(x+1)oplus cdotsoplus y)(f(l,r)=max{g(x,y)},lleq xleq yleq r) .

给定 (l,r) ,求 (f(l,r)) .


奇妙结论题。

结论:(默认低位开始 (0sim n-1) 位)

  • 如果 (l[n-1] eq r[n-1]) ,那么答案是 (11cdots1)(n) 位)
  • 否则,如果 (r[0]=1) ,答案是 (r) ;如果 (r[0]=0)(lleq r-2) ,答案是 (r+1) ;否则,答案是 (r) .

Proof

对于第一种情况显然,因为必然包含了一段 ([0111cdots1,100cdots0]) ( 成段的 (0,1) 都是 (n-1) 个),异或起来就好了。

  • (r) 为奇数

考虑归纳证明。(f(r-1,r)) 的答案显然是 (r) . 现在考虑从 (f(l,r) o f(l,r+2)) .

首先,(f(r+1,r+2)=r+2) . 其余段可以分为三种:结尾为 (r+2,r+1,r) . 根据归纳假设,第三类的答案是 (r) . 第一类的答案由一段 ([l,r]) 中的段和一段 ([r+1,r+2]) 组成,后者的异或是 (1) ,所以这一类的答案不会超过 (r+1) .

对于第二类,假设可以找到大于 (r+2) 的答案。那么一定存在一个 (r+2) 中为 (0) 的位,在答案中为 (1) ,由于 (r+2) 是奇数,这一位不是第 (0) 位。设第一个这一位为 (1) 的数是 (x) ,显然 (x) 必然是个奇数(考虑这一位后面的东西全是 (1) ,因为 (x) 是从大到小第一个出现这一位是 (1) 的),那么 ((x,r+2)) (不包含)的总数是个奇数(后面的位从偶数到 (0) )。而为了让这一位异或结果为 (1) ,从 (x) 往下的段长必然也是奇数(两个 (1) 段之间的 (0) 段长度是偶数),所以答案区间的长度是偶数,也就是说 (n-1) 位异或出来是 (0) ,不可能大于 (r+2) 。所以答案只能是 (r+2) .

归纳可得,这一种情况的答案是 (r) .

  • (r) 为偶数

(f(l,r)leq f(l,r+1)=r+1) (因为 (r+1) 是奇数),又由于 (g(r-2,r)=r+1) ,所以 (f(l,r)=r+1) .

//Author: RingweEH
int main()
{
	scanf( "%d",&n ); scanf("%s%s",a,b );
	reverse(a,a+n); reverse(b,b+n);
	if ( a[n-1]!=b[n-1] )
	{
		for ( int i=0; i<n; i++ ) printf("1"); return 0;
	}
	if ( b[0]=='1' ) { reverse(b,b+n); puts(b); return 0; }
	for ( int i=0; i<n; i++ ) l[i]=(a[i]=='1'),r[i]=(b[i]=='1');
	l[0]++;
	for ( int i=0; i<n; i++ ) l[i+1]+=l[i]/2,l[i]%=2;
	if ( l[n] ) { reverse(b,b+n); puts(b); return 0; }
	for ( int i=n-1; i>=0; i-- )
		if ( r[i]>l[i] )
		{
			r[0]++;
			for ( int i=0; i<n; i++ ) r[i+1]+=r[i]/2,r[i]%=2;
			for ( int i=(r[n]) ? n : n-1; i>=0; i-- ) printf("%d",r[i] );
			return 0;
		}
	reverse(b,b+n); puts(b);

	return 0;
}

F - Enchanted Matrix

给定 (n,m) ,将整个矩阵划分成每个大小为 (r imes c)(r|n,c|m) )的子矩阵,使得它们成对相等。求 ((r,c)) 的方案数。

交互题。询问格式为 ? h w i1 j1 i2 j2 ,询问大小为 (h imes w) 且不重叠的、左上角分别为 ((i_1,j_1),(i_2,j_2)) 的子矩阵是否相等。询问次数上限为 (3cdotlfloorlog_2(n+m) floor) .


大致思路:行列显然可以独立求出来,分开考虑各自能分的最大块数然后计算其约数,相乘即可。

对于求最大块数:先将 (n) (或者 (m) )分解质因数,然后 ( ext{Check}) :当前块长为 (len) ,能否分成 (divi) 个相同的块。如果 (divi) 只有 (2) ,那么直接一次询问就能解决。否则,设 (k)(len/divi) 即每个小块的块长,先比较第一个 (divi/2) 和第二个 (divi/2) ,再比较第一个 (divi/2) 和第二个 (divi/2) 加上 (k) 是否相等。这样做的原理在于,第二次比较的后一个矩形是第一次比较的后一个矩形往后移一个 (k) ,不难证明这样可以直接说明全部小块都相同。

//Author: RingweEH
int Query( int len,int p1,int p2 )	//查询整行/整列,另一维长为len,端点p1p2是否相等
{
	if ( !fl ) printf("? %d %d %d %d %d %d
",len,m,p1,1,p2,1 );
	else printf("? %d %d %d %d %d %d
",n,len,1,p1,1,p2 );
	fflush(stdout); int x=read(); return x;
}

//块长是len,分成divi个块,是否相等
bool Check( int len,int divi )
{
	if ( divi==2 ) return Query(len/2,1,len/2+1 );
	int x=len/divi*(divi/2);
	return Query(x,1,x+1) && Query(x,1,x+len/divi+1);
}

//对行/列计算能分的最大块数
int Work( int x )
{
	cnt=0; int nw=x;
	for ( int i=2; i<=nw; i++ )
		if ( nw%i==0 )
		{
			a[++cnt]=i,b[cnt]=0;
			while ( nw%i==0 ) b[cnt]++,nw/=i;
		}
	int res=1;
	for ( int i=1; i<=cnt; i++ )
		for ( int j=1; j<=b[i]; j++ )
			if ( Check(x/res,a[i]) ) res*=a[i];
			else break;
	return res;
}

int Count( int x )
{
	int res=0;
	for ( int i=1; i<=x; i++ ) res+=(x%i==0);
	return res;
}

int main()
{
	n=read(); m=read();
	int ans1=Work(n); fl=1;
	int ans2=Work(m);
	printf("! %d
",Count(ans1)*Count(ans2) );

	return 0;
}
天光渐亮。
原文地址:https://www.cnblogs.com/UntitledCpp/p/14496451.html