• AtCoder Grand Contest 003题解


    传送门

    (A)

    咕咕

    const int N=1005;
    char s[N];int val[N],n;
    int main(){
    	scanf("%s",s+1),n=strlen(s+1);
    	fp(i,1,n)++val[s[i]];
    	if((val['W']!=0)^(val['E']!=0))return puts("No"),0;
    	if((val['S']!=0)^(val['N']!=0))return puts("No"),0;
    	return puts("Yes"),0;
    }
    

    (B)

    不难发现每个数能和自己匹配就和自己匹配,最多只会和左边右边分别匹配一次,那么我们记(f_{i,0/1})表示考虑(i),且(i)留下了(0/1)个的方案数,从左往右(dp)即可

    typedef long long ll;
    const int N=1e5+5;const ll inf=1e18;
    int a[N],k,n;ll f[N][2],res;
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&a[i]);
    	memset(f,0xef,sizeof(f));
    	f[0][0]=0;
    	fp(i,1,n){
    		if(!a[i])f[i][0]=max(f[i-1][0],f[i-1][1]);
    		else{
    			f[i][0]=max(f[i-1][0]+(a[i]>>1),max(f[i-1][1]+(a[i]>>1),f[i-1][1]+((a[i]-1)>>1)+1));
    			f[i][1]=max(f[i-1][0]+((a[i]-1)>>1),max(f[i-1][1]+((a[i]-1)>>1),a[i]>=2?f[i-1][1]+((a[i]-2)>>1)+1:-inf));
    		}
    	}
    	printf("%lld
    ",max(f[n][0],f[n][1]));
    	return 0;
    }
    

    (C)

    重新标号之后发现一直进行操作(2)的话奇数位上的只能一直在奇数位上,那么一个偶数要到奇数位上至少需要经过一次(1)操作,把所有偶数在奇数位上和奇数在偶数位上的个数分别记下来,两个取较大值就是答案了

    const int N=1e5+5;
    int b[N],d[N],st[N],c[2],n;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&b[i]),d[i]=b[i];
    	sort(d+1,d+1+n);
    	fp(i,1,n)st[i]=lower_bound(d+1,d+1+n,b[i])-d,c[st[i]&1]+=((st[i]&1)^(i&1));
    	printf("%d
    ",max(c[0],c[1]));
    	return 0;
    }
    

    (D)

    先把每个数所有的三次因子除掉,这样之后两个相等的数就在同一个等价类,除了(1)要特判之外,其它每个数对应的不能选的数是唯一的,在分解质因子的过程中能顺便把它给算出来,然后两边取较大值加入就可以了

    分解质因子的时候如果枚举到(10^5)显然会炸,那么我们是枚举到(3000)次左右,那么剩下的数要么是一个质数要么是一个质数的平方,分类讨论一下即可

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fi first
    #define se second
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int N=3005;
    bitset<N>vis;int p[N],m;
    void init(int n=3000){
    	fp(i,2,n){
    		if(!vis[i])p[++m]=i;
    		for(R int j=1;j<=m&&1ll*i*p[j]<=n;++j){
    			vis[i*p[j]]=1;
    			if(i%p[j]==0)break;
    		}
    	}
    }
    const int M=1e5+5;
    ll a[M];int n,res;
    map<ll,int>mp;
    typedef map<ll,int>::iterator IT;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	init();
    	scanf("%d",&n);
    	R ll tmp;
    	fp(i,1,n){
    		scanf("%lld",&a[i]);
    		for(R int j=1;j<=m&&1ll*p[j]*p[j]*p[j]<=a[i];++j){
    			tmp=1ll*p[j]*p[j]*p[j];
    			while(a[i]%tmp==0)a[i]/=tmp;
    		}
    		++mp[a[i]];
    	}
    	for(IT it=mp.begin();it!=mp.end();++it){
    		if(it->fi==1)continue;
    		R ll x=it->fi,y=1;
    		fp(j,1,m)if(x%p[j]==0){
    			R int c=0;
    			while(x%p[j]==0)x/=p[j],++c;
    			(c&1)?(y*=p[j]*p[j]):(y*=p[j]);
    		}
    		if(x!=1){
    			tmp=sqrt(x);
    			tmp*tmp==x?(y*=tmp):(y*=x*x);
    		}
    		if(!mp.count(y)||it->fi<y)res+=max(it->se,mp.count(y)?mp[y]:0);
    	}
    	if(mp[1])++res;
    	printf("%d
    ",res);
    	return 0;
    }
    

    (E)

    首先对于一个(q_i),如果它后面有一个小于等于它的数那么它就没有用了,所以先用单调栈把(q_i)变成递增的(包括一开始的(n)

    然后考虑暴力,对于一个(q_i)(q_{i+1}),相当于是把(q_i)复制若干遍之后再加上(q_i)的一个前缀形成了(q_{i+1}),那么我们从后往前做,记(cnt_{i})表示(q_i)被重复的次数,以及一个栈(tmp)代表后面所有的多出来的前缀,那么对于(i),它的(cnt)就等于原来的重复次数乘上(cnt_{i+1}),以及对于之前的每一个多余的前缀(tmp_{j}),加上一个(tmp_{j}/q_{i}*tc_{j})(其中(tc_{j})表示(j)这个前缀被重复的次数),然后(tmp_j)变成(tmp_j\%q_i),这样就可以算出(cnt_i)了,而它多出来的前缀就是(q_{i}\%q_{i-1}),且这个前缀的重复次数就是(cnt_{i})

    然而这样暴力的话是(O(n^2))的,考虑优化,对于一个前缀,它会给前面的(cnt_{i})造成贡献当且仅当(q_ileq tmp_j),也就是说取模之后(tmp_j)会变小。那么对于一个前缀,我们可以二分找到它左边第一个小于等于它的位置,然后给那个位置加上对应的贡献,之后一直找就行了。因为一个数有效的取模次数不超过(O(log n)),所以总复杂度为(O(nlog nlog q_i))

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    ll q[N],st[N],cnt[N],a[N],s[N],x;int top,n,m;
    void ins(R ll x,R ll p,R int pos){
    	R int k;
    	while(x>=st[1]){
    		k=lower_bound(st+1,st+1+pos,x+1)-st-1;
    		s[k]+=x/st[k]*p,x%=st[k],pos=k-1;
    	}
    	a[x]+=p;
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d",&n,&m),top=0;
    	st[++top]=n;
    	fp(i,1,m){
    		scanf("%lld",&x);
    		while(top&&x<=st[top])--top;
    		st[++top]=x;
    	}
    	fp(i,1,top-1)cnt[i]=st[i+1]/st[i];
    	cnt[top]=cnt[top+1]=1;
    	fd(i,top,2)cnt[i]=cnt[i]*cnt[i+1]+s[i],ins(st[i]%st[i-1],cnt[i],i-1);
    	cnt[1]=cnt[1]*cnt[2]+s[1];
    	fd(i,n,1)a[i]+=a[i+1];
    	fp(i,1,n)printf("%lld
    ",(i<=st[1]?cnt[1]+a[i]:0));
    	return 0;
    }
    

    (F)

    先判断这个网格左右拼接和上下拼接是否会导致联通块个数的减少,分别记为(flag1)(flag2),并且为了方便起见我们默认初始图形为给出的网格(也就是事实上的(1)阶分形)为(0)阶分形

    如果两个都不成立,证明每次分形都不会减少联通块,记网格中总的黑点个数为(sz),那么(k)次分形之后会使联通块个数变为(sz^k)

    如果两个都成立,因为黑点四联通,所以每次分形之后联通块个数都不会减少,最后答案为(1)

    剩下两种情况中,不失一般性,假设左右拼接会导致联通块个数减少而上下不会,记((i,j))((i,j+1))同时为黑点的数量为(p),以及((i,1))((i,m))同时为黑点的数量为(q)

    假设(k)次分形之后联通块个数为(ans),那么第(k+1)次分形后,联通块个数会变为(ans imes sz-p imes q^{k-1})(这个柿子可以画个图理解)

    那么就好办了,一开始答案是(1),把柿子全部展开变成形如(((1 imes sz-p imes q^{0}) imes sz -p imes q^{1})...),变成(sz^k-p imes sum_{i=0}^{k-1}q^{i}sz^{k-1-i}),把后面那个当成一个等比数列求和就行了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int P=1e9+7;
    inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R ll y){
        R int res=1;
        for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
        return res;
    }
    int kkk(R int x,R ll y){
    	R int res=1,d=1;
    	for(;y;y>>=1,d=mul(x+1,d),x=mul(x,x))(y&1)?res=add(mul(res,x),d):0;
    	return res;
    }
    const int N=1005;
    char mp[N][N],t[N][N];int n,m,sz,p,ss,res;ll k;
    inline int ck1(){R int res=0;fp(i,1,n)res+=(mp[i][1]=='#'&&mp[i][m]=='#');return res;}
    inline int ck2(){R int res=0;fp(j,1,m)res+=(mp[1][j]=='#'&&mp[n][j]=='#');return res;}
    int main(){
    	scanf("%d%d%lld",&n,&m,&k);
    	if(k<=1)return puts("1"),0;
    	--k;
    	fp(i,1,n)scanf("%s",mp[i]+1);
    	fp(i,1,n)fp(j,1,m)sz+=mp[i][j]=='#';
    	int sz1=ck1(),sz2=ck2();
    	if(sz1&&sz2)return puts("1"),0;
    	if(!sz1&&!sz2)return printf("%d
    ",ksm(sz,k)),0;
    	if(sz2){
    		fp(i,1,n)fp(j,1,m)t[j][i]=mp[i][j];
    		swap(n,m);
    		fp(i,1,n)fp(j,1,m)mp[i][j]=t[i][j];
    		swap(sz1,sz2);
    	}
    	fp(i,1,n)fp(j,1,m-1)ss+=(mp[i][j]=='#'&&mp[i][j+1]=='#');
    //	printf("%d %d %d
    ",sz,ss,sz1);
    	res=mul(ksm(sz,k-1),kkk(mul(sz1,ksm(sz,P-2)),k-1));
    	res=dec(ksm(sz,k),mul(ss,res));
    	printf("%d
    ",res); 
    	return 0;
    }
    
  • 相关阅读:
    - (id)initWithCoder:(NSCoder *)decoder 的参数的作用
    pch定义的宏,文件引用报错,use of undeclared identifier
    tableView代理方法的调用时间,(主要是heightForRowAtIndexPath和cellForRowAtIndexPath调用时间)
    Receiver 'NSManagedObjectContext' for class message is a forward declaration 错误的解决方案
    delegate、notification、KVO的使用场景总结
    UIGestureRecognizer手势和scrollview冲突的简单解决
    CGAffineTransform中setTransform的几种类型以及注意事项
    使用"vuedraggable"插件实现列表排序功能;
    aliplayer的使用
    自制时间轴组件封装
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11348417.html
Copyright © 2020-2023  润新知