A
随便构造一下就好了。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
int T,n,a[maxn];
char s[maxn];
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d",&n);int ma=1;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),ma=max(a[i],ma);
for(int i=1;i<=ma;i++)s[i]='a',printf("%c",s[i]);printf("\n");
for(int i=1;i<=n;i++)
{
for(int j=a[i]+1;j<=ma;j++)if(s[j]=='a')s[j]='b';else s[j]='a';
for(int j=1;j<=ma;j++)printf("%c",s[j]);printf("\n");
}
}
}
B1&B2
对于每个能够永久待下去的位置,肯定选择将时间调整到潮水最高的时候,然后再继续往下走,因为此时潮水是在下降的,更利于通过后面的位置。
对于不能永久呆下去的位置,即潮水到了某个高度后就会GG,那么肯定越快走越好,求出下一个位置最早什么时候能走,然后尽早走过去即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 300010
int T,n,k,l;
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d %d %d",&n,&k,&l);
int now=k,dir=-1,ans=1;//now是现在潮水水位,dir表示潮水在下降还是上升
for(int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
if(x>l)ans=0;else x=l-x;
if(!ans)continue;
if(x>=k)now=k,dir=-1;
else if(dir==-1){
if(now>x)now=x;
else now--;
if(now==0)dir=1;
}else{
if(now<x)now++;
else ans=0;
}
}
if(ans)printf("Yes\n");
else printf("No\n");
}
}
C
容易将问题转化为图论问题,如果 A i ≠ B i A_i\neq B_i Ai=Bi,那么就将这两个字符连一条有向边。
对于一个连通块,从小到大依次转化肯定是最优的解,比如对于这样一个连通块: a → b , b → d , a → c , c → d a\to b,b\to d,a\to c,c\to d a→b,b→d,a→c,c→d,那么转化顺序为 a → b → c → d a\to b\to c\to d a→b→c→d。
所以需要的次数就是连通块大小 − 1 -1 −1,总的答案就是字符集 − - − 连通块数量。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
int T,n;
char s1[maxn],s2[maxn];
int fa[maxn],occ[maxn];
int findfa(int x){
return x==fa[x]?x:fa[x]=findfa(fa[x]);}
void link(int x,int y){
x=findfa(x);y=findfa(y);
if(x!=y)fa[y]=x;
}
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d",&n);
scanf("%s %s",s1+1,s2+1);
int ans=0;
for(int i=1;i<=20;i++)fa[i]=i,occ[i]=0;
for(int i=1;i<=n;i++)
{
if(s1[i]>s2[i]){
ans=-1;break;}
else link(s1[i]-'a'+1,s2[i]-'a'+1);
occ[s1[i]-'a'+1]=occ[s2[i]-'a'+1]=1;
}
if(ans!=-1)for(int i=1;i<=20;i++)if(occ[i])ans+=occ[i]-(i==fa[i]);
printf("%d\n",ans);
}
}
D
既然是异或,那么考虑两人最后 最高的不相同的位即可。
统计一下每一位有多少个 1 1 1,从高位往低位看,记这一位有 x x x 个 1 1 1, y y y 个 0 0 0。
假如 x ≡ 0 ( m o d 2 ) x\equiv 0\pmod 2 x≡0(mod2),那么最后两人在这一位的值一定是相同的,跳过不管。
假如 x ≡ 1 ( m o d 4 ) x\equiv 1\pmod 4 x≡1(mod4),那么先手拿一个 1 1 1 之后, 1 1 1 的数量就是 4 4 4 的倍数了,可以发现,无论 y y y 是奇数还是偶数,最后这 x − 1 x-1 x−1 个 1 1 1 一定会被两人平分,由于 x − 1 2 \dfrac {x-1} 2 2x−1 是偶数,所以不产生贡献,那么最后先手的这一位就是 1 1 1,后手的这一位就是 0 0 0,先手必胜。
假如 x ≡ 3 ( m o d 4 ) x\equiv 3\pmod 4 x≡3(mod4),如果两人轮流拿 1 1 1,那么先手必败。若 y ≡ 1 ( m o d 2 ) y\equiv 1\pmod 2 y≡1(mod2),那么先手只需要设法拿到最后一个 0 0 0,那么后手就变成了此时的先手,后手就必败了。
总结一下,只有当 x ≡ 3 ( m o d 4 ) x\equiv 3\pmod 4 x≡3(mod4) 且 y ≡ 0 ( m o d 2 ) y\equiv 0\pmod 2 y≡0(mod2) 时,后手才能赢。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
int T,n,b[40];
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d",&n);
memset(b,0,sizeof(b));
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
for(int j=0;j<31;j++)if(x>>j&1)b[j]++;
}
int ans=-1;
for(int j=30;j>=0;j--)if(b[j]%2){
if(b[j]%4==3&&(n-b[j])%2==0)ans=0;
else ans=1;
break;
}
if(ans==1)printf("WIN\n");
if(ans==0)printf("LOSE\n");
if(ans==-1)printf("DRAW\n");
}
}
E
与 C C C 题不同的是,这题的图中可能存在环,假如有环,那么解中也一定存在环。
容易发现,最优解一定不会经过一个点两次以上,对于一个连通块,设解中包含 k k k 条边,考虑用如下方法构造一个DAG:
- 依次考虑每条边 ( x , y ) (x,y) (x,y)
- 假如 x , y x,y x,y 在同一个DAG中,那么删去点 y y y
- 否则连接 x , y x,y x,y 这条边
可以发现,每一个简单环都会使得图中少一个点。设原图点数为 n n n,那么最后构造出来的DAG一定有 n − 1 n-1 n−1 条边,那么有 k − n + 1 k-n+1 k−n+1 条边会使得图中的点被删掉,那么最后图的大小就是 ∣ D A G ∣ = n − ( k − n + 1 ) = 2 n − k − 1 |DAG|=n-(k-n+1)=2n-k-1 ∣DAG∣=n−(k−n+1)=2n−k−1。
移项得到 k = 2 n − ∣ D A G ∣ − 1 k=2n-|DAG|-1 k=2n−∣DAG∣−1,要使得 k k k 最小,那么就要让 ∣ D A G ∣ |DAG| ∣DAG∣ 最大,那么 d p dp dp 一下找到图中最大的 D A G DAG DAG 即可。
注意, 2 n − k − 1 2n-k-1 2n−k−1 是一个连通块的答案,总的答案应该是 2 N − ∣ D A G ∣ m a x − 2N-|DAG|_{max}- 2N−∣DAG∣max−连通块数。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
#define maxm 20
int T,n;
char s1[maxn],s2[maxn];
int reach[maxm],cnt,fa[maxm];
int findfa(int x){
return x==fa[x]?x:fa[x]=findfa(fa[x]);}
void link(int x,int y){
x=findfa(x);y=findfa(y);
if(x!=y)fa[y]=x;
}
bool dag[1<<maxm];
int c[1<<maxm],ans;
int main()
{
for(int i=1;i<(1<<20);i++)c[i]=c[i-(i&-i)]+1;
scanf("%d",&T);while(T--)
{
scanf("%d",&n);
scanf("%s %s",s1+1,s2+1);
memset(reach,0,sizeof(reach));cnt=0;
memset(dag,false,sizeof(dag));
for(int i=0;i<20;i++)fa[i]=i;
for(int i=1;i<=n;i++)link(s1[i]-'a',s2[i]-'a'),reach[s1[i]-'a']|=(1<<(s2[i]-'a'));
for(int i=0;i<20;i++)if(i==fa[i])cnt++;
dag[0]=true;c[0]=ans=0;
for(int i=0;i<(1<<20);i++){
if(dag[i])ans=max(ans,c[i]);
for(int j=0;j<20;j++)
if(dag[i]&&(reach[j]&i)==0)dag[i|(1<<j)]=true;
}
printf("%d\n",40-ans-cnt);
}
}
F
直接看构造方法吧,知道方法之后正确性比较显然,因为后面放的数都没有前面放的数大,所以不需要考虑答案会不合法。
代码如下:
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 300
int n,m,a[maxn][maxn],ans[maxn][maxn];
int line[maxn*maxn],col[maxn*maxn];
struct par{
int x,y;}q[maxn*maxn];
int st=0,ed=0;
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++){
int ma=0;
for(int j=1;j<=m;j++)ma=max(ma,a[i][j]);
line[ma]=1;
}
for(int j=1;j<=m;j++){
int ma=0;
for(int i=1;i<=n;i++)ma=max(ma,a[i][j]);
col[ma]=1;
}
int X=0,Y=0;
for(int i=n*m;i>=1;i--)
{
X+=line[i],Y+=col[i];
if(line[i]||col[i])ans[X][Y]=i;
else ans[q[st].x][q[st].y]=i,st++;
if(line[i])for(int j=Y-1;j>=1;j--)q[ed++]=(par){
X,j};
if(col[i])for(int j=X-1;j>=1;j--)q[ed++]=(par){
j,Y};
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)printf("%d ",ans[i][j]);
printf("\n");
}
}