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
<
b
i
a_i<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
>
b
i
a_i>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());
}