AtCoder Grand Contest 026 题解

B - rng_10s:

大部分情况都很好判断,最后剩一种情况:A在mod B意义下+D,问是否存在x使得A+xD>C。
容易发现循环节长度为gcd(B,D),判一下即可。
code:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
LL A,B,C,D;
LL gcd(LL a,LL b) {return a==0?b:gcd(b%a,a);}
int main()
{
	LL T;scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld %lld %lld %lld",&A,&B,&C,&D);
		if(A<B) {printf("No\n");continue;}
		if(D<B) {printf("No\n");continue;}
		if(C>=B-1) {printf("Yes\n");continue;}
		A%=B;D=(D-B)%B;
		if(A>C) {printf("No\n");continue;}
		LL d=gcd(B,D),len=C-A;
		A=A+len/d*d;
		if(A+d<B) {printf("No\n");continue;}
		else {printf("Yes\n");continue;}
	}
}

C - String Coloring:

枚举前半段,那么后半段就要镜像分配,check一下即可。
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
LL n,ans=0,t[2][20],f[20][20],top[2];
char s[40];
void solve(LL S)
{
	top[0]=top[1]=0;
	for(LL i=0;i<n;i++)
	{
		LL c=S&(1<<i)?1:0;
		t[c][++top[c]]=i+1;
	}
	memset(f,0,sizeof(f));
	f[0][0]=1;
	for(LL i=0;i<=top[0];i++)
		for(LL j=0;j<=top[1];j++)
		{
			if(i==top[0]&&j==top[1]) continue;
			LL k=2*n-i-j;
			if(i<top[0]&&s[t[0][i+1]]==s[k]) f[i+1][j]+=f[i][j];
			if(j<top[1]&&s[t[1][j+1]]==s[k]) f[i][j+1]+=f[i][j];
		}
	ans+=f[top[0]][top[1]];
}
int main()
{
	scanf("%lld",&n);scanf("%s",s+1);
	for(LL i=0;i<(1<<n);i++) solve(i);
	printf("%lld",ans);
}

D - Histogram Coloring:

orz
显然可以发现无论最底层怎么填,上面都可以构造出来,且与是否存在连续相同颜色有关,假如存在,那么一定每一位取反,否则可以选择取反或不取反。
然后我sb的没做出来……
具体看下代码,一些细节挺好理解的。
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define fi first
#define se second
#define pi pair<LL,LL>
#define LL long long
using namespace std;
const LL inf=2e9;
const LL mod=1e9+7;
LL n,h[110];
LL pow(LL a,LL b)
{
	LL ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;b>>=1;
	}
	return ans;
}
pi solve(LL l,LL r,LL k)//l r区间大于k的部分
{
	LL Min=inf,cnt=0;pi ans;
	for(LL i=l;i<=r;i++)
	{
		if(h[i]<Min) Min=h[i],cnt=1;
		else if(h[i]==Min) cnt++;
	}
	if(cnt==(r-l+1))//矩形随便算 
	{
		ans.fi=(pow(2,r-l+1)+mod-2)%mod;
		ans.se=pow(2,Min-k-1);
		return ans;
	}
	LL re=r-l+1,s0=1,s1=1,last=0;//s0 s1是first second 
	for(LL i=l;i<=r+1;i++) if(!last&&h[i]>Min) last=i;
	else if(last&&(h[i]<=Min||i>r))
	{
		re-=i-last;
		pi tmp=solve(last,i-1,Min);
		s0=s0*(tmp.fi+4*tmp.se%mod)%mod;//*4是因为上一行可以取反,当前行亦然,2*2
		s1=s1*(2*tmp.se%mod)%mod;
		last=0;
	}
	s0=(s0+mod-s1)%mod;
	ans.fi=s0*pow(2,re)%mod;
	(ans.fi+=(s1*(pow(2,re)-2+mod)%mod)%mod)%=mod; 
	ans.se=s1*pow(2,Min-k-1)%mod;
	//因为fi可以从两种情况转移过来,se只能从s1转移过来 
	return ans;
} 
int main()
{
	scanf("%lld",&n);
	for(LL i=1;i<=n;i++) scanf("%lld",&h[i]);
	if(n==1)
	{
		printf("%lld",pow(2,h[1]));
		return 0;
	}
	LL tot=1;
	for(LL i=1;i<=n;i++) if(h[i]>h[i-1]&&h[i]>h[i+1])
	{
		(tot*=pow(2,h[i]-max(h[i-1],h[i+1])))%=mod;
		h[i]=max(h[i-1],h[i+1]);
	}
	pi ans=solve(1,n,0);
	printf("%lld",tot*((ans.fi+ans.se*2)%mod)%mod);
}

E - Synchronized Subsequence:

题解没怎么懂,去看别人代码就懂了,果然还是适合膜代码吗……
a i a_i ai表示第i个a出现位置, b i b_i bi同理。
先考虑两种简单情况:
1:对于所有i,有 a i &lt; b i a_i&lt;b_i ai<bi
显然,这样的序列的答案一定是 a b a b a b a b … abababab… abababab,因为这种情况的前缀一定是 a a a a b b b b … aaaabbbb… aaaabbbb那么肯定不如删掉中间的a优,那么做法就是找出尽量多的ab。
2:对于所有i,有 a i &gt; b i a_i&gt;b_i ai>bi
这种情况看上去麻烦一些,考虑有什么 b i , a i b_i,a_i bi,ai使得假如 b i , a i b_i,a_i bi,ai被选择了,就一定也被选择,容易发现只有当位置关系满足 b i b i + 1 a i a i + 1 b_ib_{i+1}a_ia_{i+1} bibi+1aiai+1时满足这个条件。
而不满足的话就说明两者是独立的,所以对于每个点,首先拓展出它后面无脑选的点对,然后再加上后面的最优解,取字典序最大就是答案。
当以上两种情况混合时,意会下,按照上面的策略也是最优的,dp即可。
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,sa[3010],sb[3010],ta=0,tb=0,pos[6010];
char s[6010];
string f[3010];
int main()
{
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i=1;i<=2*n;i++)
	{
		if(s[i]=='a') sa[++ta]=i,pos[i]=ta;
		else sb[++tb]=i,pos[i]=tb;
	}
	string ans;
	for(int i=n;i>=1;i--)
	{
		if(sa[i]<sb[i])
		{
			int k=i;
			for(int j=sa[i]+1;j<sb[i];j++) k=max(k,pos[j]);
			f[i]="ab"+f[k+1];
		}
		else
		{
			int k=i;
			while(k<n&&sb[k+1]<sa[k]) k++;
			for(int j=sb[i];j<=2*n;j++) if(pos[j]>=i&&pos[j]<=k) f[i]+=s[j];
			f[i]+=f[k+1];
		}
		f[i]=max(f[i],f[i+1]);
		ans=max(ans,f[i]);
	}
	printf("%s",ans.c_str());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值