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;
}
原文地址:https://www.cnblogs.com/yuanquming/p/11378620.html