• AtCoder Grand Contest 037


    Preface

    这篇咕了可能快一个月了吧,正好今天晚上不想做题就来补博客

    现在还不去复习初赛我感觉我还是挺刚的(微笑)


    A - Dividing a String

    考虑最好情况把每个字符串当作一个来看,考虑不合法的情况怎么处理

    可以很容易地发现再怎么差我长度分成(1,2,1,2,cdots)的样子就好了,因此字符串最长为(2)

    贪心地把后面的数和前面合并即可

    #include<cstdio>
    #include<cstring>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=200005;
    char s[N]; int n,ans,pos;
    int main()
    {
    	scanf("%s",s+1); n=strlen(s+1); ans=n; pos=1;
    	for (RI i=2;i<=n;++i)
    	if (s[i]==s[pos]) --ans,pos=i+2,i+=2; else pos=i;
    	return printf("%d",ans),0;
    }
    

    B - RGB Balls

    这道题我想了比C题长了一个小时你敢相信

    首先不考虑顺序,最后乘上(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),就一定要让这个(R)和它们匹配,否则就会使(sum a_i)确定的情况下让(sum c_i)变大

    换而言之,如果出现过(BG)种的一种,那么我们必须匹配成(RB)(RG),否则就会使(sum c_i)确定的情况下让(sum a_i)变小

    否则单独拿出来,顺便统计答案即可

    #include<cstdio>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=300005,mod=998244353;
    char s[N]; int n,ans,r,b,g,rb,rg,bg;
    int main()
    {
    	RI i; for (scanf("%d%s",&n,s+1),ans=i=1;i<=n;++i)
    	ans=1LL*ans*i%mod; for (i=3*n;i;--i)
    	switch (s[i])
    	{
    		case 'R':
    			if (bg) ans=1LL*ans*(bg--)%mod; else
    			if (b) ans=1LL*ans*(b--)%mod,++rb; else
    			if (g) ans=1LL*ans*(g--)%mod,++rg; else ++r;
    			break;
    		case 'G':
    			if (rb) ans=1LL*ans*(rb--)%mod; else
    			if (r) ans=1LL*ans*(r--)%mod,++rg; else
    			if (b) ans=1LL*ans*(b--)%mod,++bg; else ++g;
    			break;
    		case 'B':
    			if (rg) ans=1LL*ans*(rg--)%mod; else
    			if (r) ans=1LL*ans*(r--)%mod,++rb; else
    			if (g) ans=1LL*ans*(g--)%mod,++bg; else ++b;
    			break;
    	}
    	return printf("%d",ans),0;
    }
    
    

    C - Numbers on a Circle

    首先这种操作这么神仙可逆性的题目我们肯定要倒过来考虑,把加变成减

    然后我们发现一个十分有趣的性质,当(i)能操作使(i-1)(i+1)使不能操作的(因为一操作就变负了)

    那么换句话说此时(i)操作减去的数不会再改变,因此操作多少次可以直接除一下

    发现操作的顺序就是按照数的大小顺序操作的,因此我们把所有能操作的数扔进一个队列里,然后做完一个数考虑它两边的数能不能被操作即可

    #include<cstdio>
    #include<queue>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=200005;
    int n,a[N],b[N],pre[N],nxt[N]; long long ans; queue <int> q; bool vis[N];
    inline bool check(CI id)
    {
    	return b[id]-a[id]>=b[pre[id]]+b[nxt[id]];
    }
    int main()
    {
    	RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
    	for (i=1;i<=n;++i) scanf("%d",&b[i]),pre[i]=i-1,nxt[i]=i+1;
    	for (nxt[pre[1]=n]=i=1;i<=n;++i) if (check(i)) q.push(i),vis[i]=1;
    	while (!q.empty())
    	{
    		int nw=q.front(); vis[nw]=0; q.pop();
    		int cur=(b[nw]-a[nw])/(b[pre[nw]]+b[nxt[nw]]);
    		ans+=cur; b[nw]-=cur*(b[pre[nw]]+b[nxt[nw]]);
    		if (!vis[pre[nw]]&&check(pre[nw])) q.push(pre[nw]),vis[pre[nw]]=1;
    		if (!vis[nxt[nw]]&&check(nxt[nw])) q.push(nxt[nw]),vis[nxt[nw]]=1;
    	}
    	for (i=1;i<=n;++i) if (a[i]!=b[i]) return puts("-1"),0;
    	return printf("%lld",ans),0;
    }
    
    

    D - Sorting a Grid

    我们考虑将输入看作一个(n imes m)的二元组((x_i,y_i)),来表示(i)这个数必须从第(x_i)行走的第(y_i)行,且它们需要被放置同一列

    那么我们如果建出这样的二分图,把(x_i)(y_i)连边,跑一个最大匹配,那此时的方案可以作为第一列的方案

    然后考虑霍尔定理,我们发现此时每个点的度数都是(M),因此存在完美匹配

    考虑每次找出一个匹配用来填某一列,然后删掉对应的边重复填即可

    套路的不能再套路的题目

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<assert.h>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=205,M=100005,INF=1e9;
    int n,m,a[N][N],fr[N*N],to[N*N],b[N][N],c[N][N],p[N]; vector <int> eg[N][N];
    class Network_Flow
    {
    	private:
    		struct edge
    		{
    			int to,nxt,v;
    		}e[M]; int head[N],cur[N],s,t,cnt,dep[N],q[N];
    		inline void addedge(CI x,CI y)
    		{
    			e[++cnt]=(edge){y,head[x],1}; head[x]=cnt;
    			e[++cnt]=(edge){x,head[y],0}; head[y]=cnt;
    		}
    		#define to e[i].to
    		inline bool BFS(CI s,CI t)
    		{
    			RI H=0,T=1; memset(dep,-1,t+1<<2); dep[q[1]=s]=0;
    			while (H<T)
    			{
    				int now=q[++H]; for (RI i=head[now];i;i=e[i].nxt)
    				if (e[i].v&&!~dep[to]) dep[to]=dep[now]+1,q[++T]=to;
    			}
    			return ~dep[t];
    		}
    		inline int DFS(CI now,CI t,int dis)
    		{
    			if (now==t) return dis; int ret=0;
    			for (RI& i=cur[now];i&&dis;i=e[i].nxt)
    			if (e[i].v&&dep[to]==dep[now]+1)
    			{
    				int dist=DFS(to,t,min(e[i].v,dis));
    				dis-=dist; ret+=dist; e[i].v-=dist; e[i^1].v+=dist;
    			}
    			if (!ret) dep[now]=0; return ret;
    		}
    		#undef to
    	public:
    		inline void init(void)
    		{
    			RI i,j; memset(head,0,(t=(n<<1|1))+1<<2); cnt=1;
    			for (i=1;i<=n;++i) addedge(s,i),addedge(n+i,t);
    			for (i=1;i<=n;++i) for (j=1;j<=n;++j)
    			if (!eg[i][j].empty()) addedge(i,n+j);
    		}
    		inline void Dinic(void)
    		{
    			int ret=0; while (BFS(s,t))	memcpy(cur,head,t+1<<2),ret+=DFS(s,t,INF);
    			assert(ret==n); for (RI i=1,j;i<=n;++i)
    			for (j=head[i];j;j=e[j].nxt) if (e[j].to!=s&&!e[j].v) p[i]=e[j].to-n;
    		}
    }NF;
    int main()
    {
    	RI i,j,k; for (scanf("%d%d",&n,&m),i=1;i<=n;++i)
    	for (j=1;j<=m;++j) scanf("%d",&a[i][j]),fr[a[i][j]]=i;
    	for (i=1;i<=n;++i) for (j=1;j<=m;++j) to[(i-1)*m+j]=i;
    	for (i=1;i<=n*m;++i) eg[fr[i]][to[i]].push_back(i);
    	for (k=1;k<=m;++k) for (NF.init(),NF.Dinic(),i=1;i<=n;++i)
    	b[i][k]=c[p[i]][k]=eg[i][p[i]].back(),eg[i][p[i]].pop_back();
    	for (i=1;i<=n;++i) for (j=1;j<=m;++j) printf("%d%c",b[i][j]," 
    "[j==m]);
    	for (i=1;i<=n;++i) for (j=1;j<=m;++j) printf("%d%c",c[i][j]," 
    "[j==m]);
    	return 0;
    }
    
    

    E - Reversing and Concatenating

    考虑分类讨论,其实这题的情况还是挺好分析的

    首先为了让字典序最小,我们肯定考虑尽量复制原来字符串种字典序最小的字符

    • 若字符串的最后一个字符为最小的,连续长度为(L),那么此时的开头字符为最小值的长度就是(L imes 2^K)
    • 若字符串中间的一段最长的字符连续长度为(L),那么此时的开头字符为最小值的长度就是(L imes 2^{K-1})

    发现数据范围很小,我们直接暴枚从那个地方为结尾开始取即可,复杂度(O(n^2))

    #include<cstdio>
    #include<iostream>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=10005;
    char s[N],mi='z',ans[N],tp[N]; int n,k;
    inline bool cmp_min(char *a,char *b)
    {
    	for (RI i=1;i<=n;++i)
    	if (a[i]!=b[i]) return a[i]<b[i]; return 0;
    }
    inline void solve(char *s,int k)
    {
    	int nw=n,len=0; while (nw&&s[nw]==mi) --nw,++len;
    	while (k&&len<n) --k,len<<=1; len=min(n,len);
    	RI i; for (i=1;i<=len;++i) tp[i]=mi;
    	for (i=len+1;i<=n;++i) tp[i]=s[nw--];
    	if (cmp_min(tp,ans)) for (i=1;i<=n;++i) ans[i]=tp[i];
    }
    int main()
    {
    	RI i; for (scanf("%d%d%s",&n,&k,s+1),i=1;i<=n;++i)
    	mi=min(s[i],mi),s[(n<<1)-i+1]=s[i],ans[i]='z';
    	if (s[n]==mi) solve(s,k);
    	for (i=n+1;i<=(n<<1);++i) if (s[i]==mi) solve(s+i-n,k-1);
    	return puts(ans+1),0;
    }
    
    

    F - Counting of Subarrays

    我菜死了根本不会做,留着以后填坑,题解可以看曲明姐姐的


    Postscript

    哎呀比赛的坑可好歹是填完了,又要开新的坑了kel

  • 相关阅读:
    python抓取网页图片
    Socket通信之Java学习(一)
    python打包成window可执行程序
    linux下安装java
    SpringBoot系列:五、SpringBoot使用Actuator
    SpringBoot系列:四、SpringBoot集成JPA
    SpringBoot系列:三、SpringBoot中使用Filter
    SpringBoot系列:二、SpringBoot的配置文件
    SpringBoot系列:一、SpringBoot搭建
    .Net Core使用IConfiguration来处理Json文件
  • 原文地址:https://www.cnblogs.com/cjjsb/p/11441885.html
Copyright © 2020-2023  润新知