zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 037题解

    传送门

    (A)

    直接把每个字母作为一个字符串,如果某个串和它前面的相同,那么就把这个字母和它后面那个字母接起来。然而我并不会证明这个贪心的正确性

    //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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=2e5+5;
    char s[N];int vis[N],n,res;
    int main(){
    	scanf("%s",s+1),n=strlen(s+1);
    	fp(i,1,n){
    		++res,vis[i]=1;
    		if(s[i]==s[i-1]&&vis[i-1]){
    			if(i==n)--res;
    			++i;
    		}
    	}
    	printf("%d
    ",res);
    	return 0;
    }
    

    (B)

    考试的时候我简直就是个(zz)

    首先不考虑分给这些人的顺序,最后把答案乘上个(n!)就行了

    (sum (c_i-a_i)=sum c_i-sum a_i),那么就是在(sum c_i)确定的情况下使(sum a_i)最大,或者在(sum a_i)确定的情况下使(sum c_i)最小

    从后往前做,考虑现在要匹配一个(R),那么如果之前出现过(BG),我们肯定要把这个和前面的匹配,否则会使(sum a_i)确定的情况下使(sum c_i)变大

    如果出现过(B)(G),那么我们肯定要匹配成(RB)(RG),否则如果加在空串的后面,就会是(sum c_i)不变的情况下(sum a_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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int P=998244353;
    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 int y){
    	R int res=1;
    	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    	return res;
    }
    const int N=5e5+5;
    char s[N];int r,b,g,rb,bg,rg,res,n;
    int main(){
    	scanf("%d%s",&n,s+1);
    	res=1;fp(i,1,n)res=mul(res,i);
    	fp(i,1,n*3){
    		switch(s[i]){
    			case 'R':{
    				if(bg)res=mul(res,bg--);
    				else if(b)res=mul(res,b--),++rb;
    				else if(g)res=mul(res,g--),++rg;
    				else ++r;
    				break;
    			}
    			case 'G':{
    				if(rb)res=mul(res,rb--);
    				else if(r)res=mul(res,r--),++rg;
    				else if(b)res=mul(res,b--),++bg;
    				else ++g;
    				break;
    			}
    			case 'B':{
    				if(rg)res=mul(res,rg--);
    				else if(r)res=mul(res,r--),++rb;
    				else if(g)res=mul(res,g--),++bg;
    				else ++b;
    				break;
    			}
    		}
    	}
    	printf("%d
    ",res);
    	return 0;
    }
    

    (C)

    首先倒过来考虑,变成从(B_i)变成(A_i),且定义一次操作为(B_i-=B_{i-1}+B_{i+1}),那么我们发现当(i)可以操作的时候,(i-1)(i+1)是不能操作的,所以对于每个(i)肯定都是把它操作到不能操作为止,最后有可能会使(i-1)(i+1)变得可操作,那么就把这两个数加进待选队列里就好了

    //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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int N=5e5+5,M=5e7+5;
    int a[N],b[N],las[N],nxt[N],n;ll res;
    inline bool ck(R int x){return (b[x]-a[x])>=b[las[x]]+b[nxt[x]];}
    int q[M];
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)las[i]=i-1,nxt[i]=i+1;las[1]=n,nxt[n]=1;
    	fp(i,1,n)scanf("%d",&a[i]);fp(i,1,n)scanf("%d",&b[i]);
    	int h=1,t=0,u,k;
    	fp(i,1,n)if(ck(i))q[++t]=i;
    	while(h<=t){
    		u=q[h++];
    		k=(b[u]-a[u])/(b[las[u]]+b[nxt[u]]);
    		res+=k,b[u]-=k*(b[las[u]]+b[nxt[u]]);
    //		printf("%d %d
    ",u,b[u]);
    		if(ck(las[u]))q[++t]=las[u];
    		if(ck(nxt[u]))q[++t]=nxt[u];
    		if(ck(u))q[++t]=u;
    	}
    	fp(i,1,n)if(a[i]!=b[i])return puts("-1"),0;
    	return printf("%lld
    ",res),0;
    }
    

    (D)

    把输入看做一个(n imes m)的二元组((x_i,y_i))表示(i)这个数需要从第(x_i)行到第(y_i)行,且它们需要在同一列

    那么我们在一个二分图里把左边的(x_i)向右边的(y_i)连边,跑一个最大匹配,此时的方案可以作为第一列的方案,同理也可以求出后面几列的方案

    然后就没了

    还以为需要一遍最大匹配跑出答案完全没想到可以多跑几遍

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define pb push_back
    #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)
    #define gg(u) for(int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    inline int min(R int x,R int y){return x<y?x:y;}
    const int N=1005,inf=0x3f3f3f3f,M=5e6+5;
    struct eg{int v,nx,w;}e[M];int head[N],tot;
    inline void add(R int u,R int v,R int w){
    	e[++tot]={v,head[u],w},head[u]=tot;
    	e[++tot]={u,head[v],0},head[v]=tot;
    }
    int cur[N],dis[N],q[N],S,T;
    bool bfs(){
    	int h=1,t=0,u;
    	memset(dis,0x3f,(T-S+1)<<2);
    	memcpy(cur,head,(T-S+1)<<2);
    	q[++t]=S,dis[S]=0;
    	while(h<=t){
    		u=q[h++];
    		go(u)if(e[i].w&&dis[v]==inf){
    			dis[v]=dis[u]+1,q[++t]=v;
    			if(v==T)return true;
    		}
    	}
    	return false;
    }
    int dfs(int u,int lim){
    	if(u==T||!lim)return lim;
    	int fl=0,f;
    	gg(u)if(dis[v]==dis[u]+1&&(f=dfs(v,min(lim,e[i].w)))){
    		fl+=f,lim-=f,e[i].w-=f,e[i^1].w+=f;
    		if(!lim)break;
    	}
    	if(!fl)dis[u]=inf;return fl;
    }
    int pp[N],mat[N][N];
    int n,m;
    void dinic(){
    	memset(head,0,(T-S+1)<<2),tot=1;
    	fp(i,1,n)add(S,i,1),add(i+n,T,1);
    	fp(i,1,n)fp(j,1,n)if(mat[i][j])add(i,j+n,1);
    	R int res=0;
    	while(bfs())res+=dfs(S,inf);
    	fp(u,1,n)go(u)if(v!=S&&!e[i].w)pp[u]=v-n;
    }
    vector<int>c[N][N];
    int mp[N][N],fr[N*N],to[N*N],f[N][N],g[N][N];
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d",&n,&m),S=0,T=(n<<1|1);
    	fp(i,1,n)fp(j,1,m)scanf("%d",&mp[i][j]),fr[mp[i][j]]=i;
    	fp(i,1,n)fp(j,1,m)to[(i-1)*m+j]=i;
    	fp(i,1,n*m)c[fr[i]][to[i]].pb(i);
    	fp(k,1,m){
    		fp(i,1,n)fp(j,1,n)mat[i][j]=!c[i][j].empty();
    		dinic();
    		for(R int tmp,i=1;i<=n;++i){
    			tmp=c[i][pp[i]].back(),c[i][pp[i]].pop_back();
    			f[i][k]=tmp,g[pp[i]][k]=tmp;
    		}
    	}
    	fp(i,1,n)fp(j,1,m)printf("%d%c",f[i][j]," 
    "[j==m]);
    	fp(i,1,n)fp(j,1,m)printf("%d%c",g[i][j]," 
    "[j==m]);
    	return 0;
    }
    

    (E)

    首先为了字典序最小,我们肯定是让最小的字母接在最前面,记开头的连续最小字母个数为(len)

    如果(s[n])是最小字母,且以(s[n])为结尾的连续的最小字母长度为(L),那么(len)要和(L imes 2^{k})取较大值

    如果(s[i](i eq n))是最小字母,且包含(s[i])的连续的最小字母长度为(L),那么(len)要和(L imes 2^{k-1})取较大值

    然后取这其中字典序最小的即可,复杂度(O(n^2)),建议画图理解

    //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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=10005;
    char s[N],t[N],ans[N],mn,mx;int n,k;
    bool cmp(char *s,char *t){
    	fp(i,1,n)if(s[i]!=t[i])return s[i]<t[i];
    	return false;
    }
    void solve(char *s,int k){
    	R int now=n,len=0;
    	while(now&&s[now]==mn)--now,++len;
    	while(k&&len<n)len<<=1,--k;
    	if(len>=n){
    		fp(i,1,n)ans[i]=mn;
    		printf("%s
    ",ans+1);exit(0);
    	}
    	fp(i,1,len)t[i]=mn;fp(i,len+1,n)t[i]=s[now--];
    	if(cmp(t,ans))fp(i,1,n)ans[i]=t[i];
    }
    int main(){
    	scanf("%d%d%s",&n,&k,s+1);
    	mn='z',mx='a';
    	fp(i,1,n)cmin(mn,s[i]),cmax(mx,s[i]),ans[i]='z',s[(n<<1)-i+1]=s[i];
    	if(mn==mx)return printf("%s
    ",s+1),0;
    	if(s[n]==mn)solve(s,k);
    	fp(i,n,n<<1)if(s[i]==mn)solve(s+i-n,k-1);
    	printf("%s
    ",ans+1);
    	return 0;
    }
    

    (F)

    好神奇的题啊……orzxyx

    首先考虑如何判断一个区间是否合法

    如果区间长度为(1)必然合法

    如果区间所有数都一样且区间长度大于等于(L)必然合法

    否则设(m)为当前序列最小值,把所有的极长的连续的(m)给合起来合成(m+1),如果不能合说明不合法,否则递归下去就行了

    一个数如果被合了之后整个序列长度就会减少(L-1),所以总的复杂度是(o(nlog n))

    那么把它扩展一下,因为计算区间([l,r])的时候会把它的子区间也计算在内,如果我们对于每个合法区间能记一个(x_l,x_r,y_l,y_r),使得这个区间的贡献是(x_l imes y_r),那么我们只需要对所有合法的区间([l,r])([x_l,y_r])的和就行了

    还是考虑上面那个方法,唯一需要扩展的地方就是把(m)合起来变成(m+1)这个操作

    对于一段连续的(m),其中第(1,2,3,...,L-1)个元素不能作为新的区间的左端点,(L,L+1,L+2,...,2L-1)可以产生一个(m+1),之后是(2)个……

    具体细节看代码……

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fi first
    #define se second
    #define pb push_back
    #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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=2e5+5;
    typedef pair<int,int> pi;
    typedef long long ll;
    int n,l,a[N];pi b[N];ll res=0;
    ll calc(R vector<pi> &v){
    	R ll res=0,sum=0;
    	for(R int i=0,j=l-1,s=v.size();j<s;++i,++j)
    		sum+=v[i].fi,res+=sum*v[j].se;
    	return res;
    }
    vector<pair<pi,pi> >f,g;
    int main(){
    	scanf("%d%d",&n,&l);
    	fp(i,1,n)scanf("%d",&a[i]),b[i]=pi(a[i],i);
    	sort(b+1,b+1+n);
    	res=n;
    	for(R int val=0,pos=1;233;f=g){
    		if(f.empty()){
    			if(pos>n)break;
    			val=b[pos].fi;
    		}else ++val;
    		while(val==b[pos].fi)f.pb(make_pair(pi(b[pos].se,b[pos].se),pi(1,1))),++pos;
    		g.clear(),sort(f.begin(),f.end());
    		for(R int i=0,j=0,s=f.size();i<s;i=++j){
    			while(j+1<s&&f[j+1].fi.fi==f[j].fi.se+1)++j;
    			R int len=j-i+1,cnt=len/l;
    			if(cnt){
    				vector<pi>tmp,ff,gg;
    				fp(k,i,j)tmp.pb(f[k].se);
    				res+=calc(tmp);
    				fp(k,1,cnt){
    					ff.pb(pi(f[i].fi.fi+k-1,(k==cnt)?f[j].fi.se:f[i].fi.fi+k-1));
    					gg.pb(pi(0,0));
    				}
    				fp(k,i,j){
    					R int tl=k-i+1,tr=j-k+1;
    					if(tl>=l)gg[tl/l-1].se+=f[k].se.se;
    					if(tr>=l)gg[cnt-tr/l].fi+=f[k].se.fi;
    				}
    				res-=calc(gg);
    				for(R int k=0;k<cnt;++k)g.pb(make_pair(ff[k],gg[k]));
    			}
    		}
    	}
    	printf("%lld
    ",res);
    	return 0;
    }
    
  • 相关阅读:
    xadmin列表页图片缩放(大图小图切换显示)
    xadmin中添加Action类
    xadmin的模块自动注册(注册版本)
    nginx内容清除
    Invalid template name in 'extends' tag: ''. Got this from the 'base_template' variable.
    django xadmin 导入功能添加
    python将excel读取的日期文本数字转为日期(5位数字时间戳)
    数据库主从设置
    django admin 增加查看权限
    oss图片上传失败
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11378620.html
Copyright © 2011-2022 走看看