zoukankan      html  css  js  c++  java
  • 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;
    }
    
    天光渐亮。
  • 相关阅读:
    类似吸顶功能解决ios不能实时监听onscroll的触发问题
    js 移动端识别手机号码
    H5输入框实时记录文字个数
    C语言指针和数组
    PHP变量
    PHP 的引用计数基础知识
    PHP提高效率的经验
    JS内置Function对象详解
    Javascript小细节总结
    浅析C++中内存分配的方式
  • 原文地址:https://www.cnblogs.com/UntitledCpp/p/14496451.html
Copyright © 2011-2022 走看看