AtCoder Grand Contest 037 题解

T1:不想说…
T2:给一个R,G,B构成的序列,长度3n,各有n个
分给n个人,位置为a,b,c的话
答案为求 ∑ m a x ( a , b , c ) − m i n ( a , b , c ) \sum max(a,b,c)-min(a,b,c) max(a,b,c)min(a,b,c)为最小值时的方案数
n ⩽ 1 0 5 n\leqslant 10^5 n105
这是一道贪心题
维护当前匹配的R,G,B,RG,BG,RB,空的个数
然后加入一个字符时尽量加个数多的
例如,加入R,尽量加BG,不行就加B,G,再不行就空集
一个结论是:R,G,B不可能同时都有
RG,BG,RB不可能同时都有
加入时乘上对应的个数即可

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int Mod=998244353;
int n;
#define Maxn 300010
char str[Maxn];
int fact=1,Ans=1;
int a[7];//empty R G B RG BG RB
int main(){
	scanf("%d",&n);
	for(register int i=1;i<=n;++i)fact=1ll*fact*i%Mod;
	scanf("%s",str+1);
	for(register int i=1;i<=3*n;++i){
		if(str[i]=='R'){
			if(a[5]){
				Ans=1ll*Ans*a[5]%Mod;
				a[5]--;
				continue;
			}
			if(a[2]){
				Ans=1ll*Ans*a[2]%Mod;
				a[2]--;
				a[4]++;
				continue;
			}
			if(a[3]){
				Ans=1ll*Ans*a[3]%Mod;
				a[3]--;
				a[6]++;
				continue;
			}
			a[1]++;
		}else if(str[i]=='G'){
			if(a[6]){
				Ans=1ll*Ans*a[6]%Mod;
				a[6]--;
				continue;
			}
			if(a[1]){
				Ans=1ll*Ans*a[1]%Mod;
				a[1]--;
				a[4]++;
				continue;
			}
			if(a[3]){
				Ans=1ll*Ans*a[3]%Mod;
				a[3]--;
				a[5]++;
				continue;
			}
			a[2]++;
		}else{
			if(a[4]){
				Ans=1ll*Ans*a[4]%Mod;
				a[4]--;
				continue;
			}
			if(a[1]){
				Ans=1ll*Ans*a[1]%Mod;
				a[1]--;
				a[6]++;
				continue;
			}
			if(a[2]){
				Ans=1ll*Ans*a[2]%Mod;
				a[2]--;
				a[5]++;
				continue;
			}
			a[3]++;
		}
	}
	printf("%d\n",1ll*Ans*fact%Mod);
	return 0;
}

T3:n个数,每次可以选第i个数,将其变成第i-1(这是循环序列),i,i+1个数的和
给定A,B,问A序列最少几步到达B。
n ≤ 2 ∗ 1 0 5 , 1 ⩽ A i , B i ⩽ 1 0 9 n\leq 2*10^5,1\leqslant A_i,B_i\leqslant 10^9 n2105,1Ai,Bi109
反向考虑,然后每次从最大值考虑
剩下的就很好想了
O ( n l o g 2 n l o g 2 V ) O(nlog_2nlog_2V) O(nlog2nlog2V)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
int n;
#define Maxn 200010
int a[Maxn],b[Maxn];
struct Data{
	int id,val;
	bool operator <(const Data &z)const{return val<z.val;}
};
priority_queue<Data> Q;
int main(){
	scanf("%d",&n);
	for(register int i=0;i<n;++i)scanf("%d",&b[i]);
	for(register int i=0;i<n;++i){
	    scanf("%d",&a[i]);
	    if(a[i]<b[i]){
	    	puts("-1");
	    	return 0;
		} 
	}
	for(register int i=0;i<n;++i)
	if(a[i]!=b[i])Q.push((Data){i,a[i]});
	long long ans=0;
	while(!Q.empty()){
		int x=Q.top().id;
		Q.pop();
		int y1=(x-1+n)%n,y2=(x+1)%n;
	    int tmp=(a[x]-b[x])/(a[y1]+a[y2]);
	    ans+=tmp;
	    if(tmp==0&&a[x]!=b[x]){puts("-1");return 0;}
	    a[x]-=tmp*(a[y1]+a[y2]);
	    if(a[x]!=b[x])Q.push((Data){x,a[x]});
	}
	printf("%lld\n",ans);
	return 0;
}

T4:这里不妨采用简化题意
给一个n*m的矩阵,n行每行是1~n的数构成
1~n的数在整个矩阵中各出现m次
给每行重排列,使得每一列是1~n的一个排列
考虑逐步确定每一列,即递归
如果现在是n行m’列
容易建出一个二分图
我们采用Hall定理得知它一定有完美匹配

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int inf=2147483647;
int n,m;
#define V 205
#define E 30010
int head[V],v[E],w[E],cap[E],nxt[E],cur[V],tot=0;
inline void add_edge(int s,int e,int t){
	tot++;v[tot]=e;w[tot]=1;cap[tot]=t;nxt[tot]=head[s];head[s]=tot;
	tot++;v[tot]=s;w[tot]=0;cap[tot]=t;nxt[tot]=head[e];head[e]=tot;
}
int a[V][V];
vector<int> g[V][V];
int res[V][V],tmp[V];
inline int col(int x){
	if(x%m==0)return x/m;
	return x/m+1;
}

int S,T;
int Q[V],hd,tl,d[V];
bool bfs(){
	memset(d,-1,sizeof(int)*(2*n+3));
	d[S]=0;
	hd=tl=0;
	Q[tl++]=S;
	while(hd<tl){
		int u=Q[hd];
		hd++;
		for(int i=head[u];i;i=nxt[i])
		    if(w[i]&&d[v[i]]==-1){
		    	d[v[i]]=d[u]+1;
		    	Q[tl++]=v[i];
				if(v[i]==T)return true; 
			}
	}
	return false;
}
int dfs(int u,int flow){
	if(u==T||!flow)return flow;
	int ans=0,res;
	for(int &i=head[u];i;i=nxt[i])
	   if(w[i]&&d[v[i]]==d[u]+1&&(res=dfs(v[i],min(flow,w[i])))){
	   	    w[i]-=res;
	   	    if(i&1)w[i+1]+=res;
	   	    else w[i-1]+=res;
	   	    ans+=res;
	   	    flow-=res;
	   	    if(!flow)break;
	   }
	return ans;
}

inline void solve(int x){
	S=2*n+1;T=2*n+2;
	memset(head,0,sizeof(int)*(2*n+3));tot=0;
	for(register int i=1;i<=n;++i)
	    for(register int j=1;j<=n;++j)
	        if(g[i][j].size())add_edge(i,j+n,g[i][j][g[i][j].size()-1]);
    for(register int i=1;i<=n;++i){
    	add_edge(S,i,0);
    	add_edge(i+n,T,0);
	}
	int haha=0;
	memcpy(cur,head,sizeof(int)*(2*n+3));
	while(bfs()){
		haha+=dfs(S,inf);
		memcpy(head,cur,sizeof(int)*(2*n+3)); 
	}
	int tmp=-1;
	for(register int i=1;i<=n;++i)
	    for(register int j=1;j<=n;++j){
	    	if(g[i][j].size()){
	    		tmp+=2;
	    	if(w[tmp]==0){
	    		res[i][x]=cap[tmp];
	    		g[i][j].pop_back();
			}
		}
		}
}

inline void rd(int &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
}

int main(){
	rd(n);rd(m);
	for(register int i=1;i<=n;++i)
	    for(register int j=1;j<=m;++j){
	    	rd(a[i][j]);
	    	g[i][col(a[i][j])].push_back(a[i][j]);
		}
	for(register int i=1;i<=m;++i)solve(i);
	for(register int i=1;i<=n;++i){
		for(register int j=1;j<=m;++j)printf("%d ",res[i][j]);
		puts("");
	}
	for(register int i=1;i<=m;++i){
		for(register int j=1;j<=n;++j)tmp[j]=res[j][i];
		sort(tmp+1,tmp+n+1);
		for(register int j=1;j<=n;++j)res[j][i]=tmp[j];
	}
	for(register int i=1;i<=n;++i){
		for(register int j=1;j<=m;++j)printf("%d ",res[i][j]);
		puts("");
	}
	return 0;
} 

T5:给出一个长度为n的串S,每次从SS’(即S的reverse)中选取一个长度为n的字串
操作K次,求字典序最小的结果
考虑最小字符a,尽量最大化前缀
假设最长连续的a为L,末尾的a有G个
那么最后a的前缀 m a x ( L ∗ 2 K − 1 , G ∗ 2 K ) max(L*2^{K-1},G*2^K) max(L2K1,G2K)
确定好第一步后,保证这样最后a的前缀最大化
复杂度 O ( n 2 ) O(n^2) O(n2)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,K;
#define Maxn 10010
char S[Maxn],Ans[Maxn];
int L=0,H;
int G=0;
char c='z';
char ch[Maxn];
int sum[Maxn];

inline void solve(){
	for(register int i=1;i<=n-L;++i)ch[i]=ch[L-H+i];
	for(register int i=n-L+1;i<=n;++i)ch[i]=c;
	reverse(ch+1,ch+n+1);
}

int main(){
	scanf("%d%d",&n,&K);
	scanf("%s",S+1);
	for(register int i=1;i<=n;++i)Ans[i]=c;
	for(register int i=1;i<=n;++i)
	    if(S[i]<c)c=S[i];
    int at;
    bool flag=false;
	for(register int i=1;i<=n;i=at)
        if(S[i]==c){
         	at=i;
         	while(at<=n&&S[at]==c)at++;
         	if(at==n+1)G=at-i;
         	if(at-i>=L){
         		L=at-i;
         		if(at==n+1)flag=true;
			}
		}else at=i+1;
	H=L;
	if(G*2>H){
		H=G;L=G; 
		flag=true;
	} 
	if(!flag)K--;
	for(register int i=1;i<=K;++i){
		L*=2;
		if(L>=n){
			for(register int j=1;j<=n;++j)printf("%c",c);
			return 0;
		}
	}
	if(flag){
		for(register int i=1;i<=n;++i)ch[i]=S[i];
		solve();
		bool Ha=false;
		for(register int i=1;i<=n;++i){
			if(Ha==false&&ch[i]>Ans[i])break;
			if(ch[i]<Ans[i])Ha=true;
			if(Ha)Ans[i]=ch[i];
		}
	}else{
		for(register int i=n+1;i<=2*n;++i)S[i]=S[2*n+1-i];
		sum[0]=0;
		for(register int i=1;i<=2*n;++i){
			sum[i]=sum[i-1];
			if(S[i]==c)sum[i]++;
		}
		for(register int i=n;i<=2*n;++i)
		    if(sum[i]-sum[i-H]==H){
		    	for(register int j=1;j<=n;++j)ch[j]=S[i-n+j];
		    	solve();
		    	bool Ha=false;
		    	for(register int i=1;i<=n;++i){
		    		if(Ha==false&&ch[i]>Ans[i])break;
		    		if(ch[i]<Ans[i])Ha=true;
		    		if(Ha)Ans[i]=ch[i];
				}
			}
	}
	for(register int i=1;i<=n;++i)printf("%c",Ans[i]);
	return 0;
}

T6:突然想咕咕咕了…

#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
#include<vector>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,L;
#define Maxn 200010
ll Ans=0;
struct data{
    int val,pos;
    bool operator <(const data &z)const{return val==z.val?pos<z.pos:val<z.val;}
}seq[Maxn];
struct Data{
    int l,r;
	int x,y;
	bool operator <(const Data &z)const{return l<z.l;}
};
vector<Data> A,B,vec2;
vector<pair<int,int> > vec1;
int num[Maxn];

inline ll calc(vector< pair<int,int> > &a){
	ll res=0,sum=0;
	for(int i=L-1,j=0;i<a.size();i++,j++){
		sum+=a[j].first;
		res+=a[i].second*sum;
	}
	return res;
}

inline void rd(int &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	} 
}

int main(){
	rd(n);rd(L);
	for(register int i=1;i<=n;++i){
		rd(num[i]);
		seq[i]=(data){num[i],i};
	}
	sort(seq+1,seq+n+1);
	ll Ans=n;
	int at=1,tmp;
	while(true){
		if(A.empty()){
			if(at>n)break;
			else tmp=seq[at].val;
		}else tmp++;
		while(tmp==seq[at].val)A.push_back((Data){seq[at].pos,seq[at].pos,1,1}),at++;
		B.clear();sort(A.begin(),A.end());
		for(int i=0,j;i<A.size();i=j+1){
			j=i;
			while(j+1<A.size()&&A[j+1].l==A[j].r+1)j++;
			int len=j-i+1,cnt=len/L;
			if(cnt){
				vec1.clear();
				vec2.clear();
				for(register int k=i;k<=j;++k)vec1.push_back(make_pair(A[k].x,A[k].y));
				Ans+=calc(vec1);
				for(register int k=1;k<=cnt;++k)
					vec2.push_back((Data){A[i].l+k-1,(k==cnt)?A[j].r:A[i].l+k-1,0,0});
				for(register int k=i;k<=j;++k){
					int tl=k-i+1,tr=j-k+1;
					if(tl>=L)vec2[tl/L-1].y+=A[k].y;
					if(tr>=L)vec2[cnt-tr/L].x+=A[k].x;
				}
				vec1.clear();
			    for(register int k=0;k<cnt;++k)vec1.push_back(make_pair(vec2[k].x,vec2[k].y));
				Ans-=calc(vec1);
				for(register int k=0;k<cnt;++k)B.push_back(vec2[k]);
			}
		}
		A.clear();
		for(int i=0;i<B.size();++i)A.push_back(B[i]);
	}
	printf("%lld\n",Ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值