前言
这几天在家忙着给弟弟妹妹补课,可能脑子转得不那么灵活,比赛打得很一般。
Standings:3227
题目链接:Dashboard - Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2) - Codeforces
A. Diverse Game
题意:
给一个 n*m 的矩阵,每个数都不相同,要求打乱位置,使得每个数都不在原来的位置上。
思路:
显然只有 1*1 的矩阵不合法,对于合法的矩阵整体往右和往下挪一位即可(本人代码写得略微复杂,可以参考官方代码)
#include<cstdio>
#include<cstring>
using namespace std;
int T,n,m,a[12][12],b[12][12];
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d%d",&n,&m);
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= m;++ j)
scanf("%d",&a[i][j]);
if(n == 1 && m == 1)
{
printf("-1\n");
continue;
}
if(m != 1)
{
for (int i = 1;i <= n;++ i)
{
for (int j = 1;j < m;++ j) b[i][j] = a[i][j + 1];
b[i][m] = a[i][1];
}
}
else
{
for (int j = 1;j <= m;++ j)
{
for (int i = 1;i < n;++ i) b[i][j] = a[i + 1][j];
b[n][j] = a[1][j];
}
}
for (int i = 1;i <= n;++ i)
{
for (int j = 1;j <= m;++ j) printf("%d ",b[i][j]);
printf("\n");
}
}
return 0;
}
B. Fun Game
题意:
给两个 01 串 s 和 t ,每次操作可以选择一段区间 [l,r] ,对所有 l <= i <= r 的 i ,将 替换为
,询问每一组 s 能否通过若干次操作变成 t 。
思路:
首先显然我们可以让 s 的每一位都变成 0 (让每一位异或自己即可)。
于是我们只需考虑能否把 t 中的 1 对应的 都变成 1 。有了上述条件,我们就可以肆无忌惮地先把 s 尽可能多地变成 1 ,那么很显然如果 s 的这一位之前存在至少一个 1 ,我们就可以把 s 的这一位也变成 1 。
#include<cstdio>
#include<cstring>
using namespace std;
#define N 200005
int T,n,a[N],b[N];
char s1[N],s2[N];
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d",&n);
scanf("%s%s",s1 + 1,s2 + 1);
for (int i = 1;i <= n;++ i) a[i] = s1[i] - '0',b[i] = s2[i] - '0';
int now = 0;
int flag = 1;
for (int i = 1;i <= n;++ i)
{
if(a[i] == 1)
{
now = 1;
continue;
}
if(!b[i]) continue;
if(!now)
{
flag = 0;
break;
}
}
if(flag) printf("YES\n");
else printf("NO\n");
}
return 0;
}
C. Hungry Games
题意:
给你 n 个蘑菇,每个蘑菇都有一个剧毒值。你的角色可以选择一个区间 [l,r] ,然后从左到右依次吃下区间内的每一个蘑菇,你的角色有一个初始值为 0 的剧毒值,剧毒值的 bar 为 x ,吃下每一个蘑菇之后会依次发生如下过程:
1. 假设当前蘑菇的剧毒值为 k ,则角色的剧毒值增加k
2. 若此时 g <= x ,过程继续;若此时 g > x ,则重置 g = 0 ,过程继续。
求所有可以让 g 不为 0 的区间数目。
思路:
注意到一个合法区间中途可能出现过多次 g = 0 的时候,每次 g = 0 之后可以看成是角色游戏的重新开始,根据这个作为切入点。
我们可以枚举区间的左端点 l ,二分找到 l 右边第一个让 g 为 0 的右端点,统计以这个位置为右端点的使得 g = 0 的区间数目,然后枚举到这个右端点的位置作为左端点的时候,再把方案数往右扩散累加即可。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define N 200005
int T,n,x;
long long a[N],pre[N],tot[N],ans;
int main()
{
scanf("%d",&T);
while (T --)
{
cin >> n >> x;
pre[0] = ans = 0ll;
for (int i = 1;i <= n;++ i) scanf("%lld",&a[i]),pre[i] = pre[i - 1] + a[i],tot[i] = 1ll;
for (int i = 1;i <= n;++ i)
{
int l,r,mid,now;
l = i,r = n,now = 0;
while (l <= r)
{
mid = (l + r) >> 1;
if(pre[mid] - pre[i - 1] <= x) now = mid,l = mid + 1;
else r = mid - 1;
}
if(!now)
{
tot[i + 1] += tot[i];
continue;
}
ans += 1ll * (now - i + 1) * tot[i];
tot[now + 2] += tot[i];
}
printf("%lld\n",ans);
}
return 0;
}
D. Funny Game
题意:
给一个 n 个点的图和一个长度为 n 的序列 a ,起初图里没有任何边,你需要通过 n - 1 次操作向图里加边,判断是否可以使 n 个点连成一个连通块。若可以,输出每次操作连接的两个节点。
将操作从 1 到 n - 1 标号,标号为 x 的操作可以执行如下操作:
1. 选择两个不同的点 u 和 v ,满足 | au - av | 可以被 x 整除。
2. 在节点 u 和 v 之间连一条边
思路:
由于是离线的操作,我们可以考虑倒着从标号为 n - 1 到 1 的操作执行。
题目条件 “ | au - av | 可以被 x 整除 ” 可以转换为 au 和 av 对 x 取余相等,即 au av (mod x)。假设当前场上还剩 m 个连通块,我们执行标号为 m - 1 的操作,每个连通块任意派出一个点作为代表,那么这 m 个点一定有至少两个点对 m - 1 取余是相等的,我们直接连边即可。
从这个算法可以看出对任意的序列都有至少一种合法的连边方式使得所有节点都在一个连通块内。
#include<cstdio>
#include<cstring>
using namespace std;
#define N 2005
int T,n,a[N],vis[N],bz[N],ans[N][2];
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d",&n);
for (int i = 1;i <= n;++ i) scanf("%d",&a[i]),vis[i] = 0;
for (int i = n - 1; i ;-- i)
{
for (int j = 0;j <= n;++ j) bz[j] = 0;
for (int j = 1;j <= n;++ j)
if(!vis[j])
{
int now = a[j] % i;
if(bz[now])
{
ans[i][0] = bz[now],ans[i][1] = j,vis[j] = 1;
break;
}
else bz[now] = j;
}
}
printf("YES\n");
for (int i = 1;i < n;++ i) printf("%d %d\n",ans[i][0],ans[i][1]);
}
return 0;
}
E. Wooden Game
题意:
给一片森林,告诉你每棵树的大小,你需要进行若干次操作砍掉整片森林。
每次操作选择一棵树,砍掉这棵树上的任意个节点,记砍掉的节点数为 x ,求所有操作得到 x 的按位或的最大值。
思路:
对于一颗树的大小 x ,它要么覆盖二进制下最高位上的 1 ,要么覆盖二进制下比最高位低位的所有 1 。
那么有个很显然的贪心思路:把所有树的大小从大到小进行排序,从大到小进行按位或操作,若当前二进制下最高位对应的答案还是 0 ,那么就用 size 本身进行按位或(即直接砍掉整棵树),否则就让二进制下比最高位低的那些位的答案都覆盖成 1 。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000006
int T,n,pw[25],ans;
struct Node
{
int val,num;
int b[25];
}a[N];
int cmp(Node x,Node y) { return x.val > y.val ; }
int main()
{
pw[0] = 1;
for (int i = 1;i <= 22;++ i) pw[i] = pw[i - 1] << 1;
scanf("%d",&T);
int now = 0;
while (T --)
{
now ++;
scanf("%d",&n),ans = 0;
for (int i = 1;i <= n;++ i)
{
scanf("%d",&a[i].val),a[i].num = 0;
for (int j = 1,x;j < a[i].val;++ j) scanf("%d",&x);
for (int j = 22;j >= 0;-- j)
if(pw[j] <= a[i].val)
{
a[i].val -= pw[j];
a[i].b[++ a[i].num] = pw[j];
}
}
sort(a + 1,a + n + 1,cmp);
for (int i = 1;i <= n;++ i)
{
for (int j = 1;j <= a[i].num;++ j)
{
if(!(ans & a[i].b[j])) ans |= a[i].b[j];
else ans |= a[i].b[j] - 1;
}
}
printf("%d\n",ans);
}
return 0;
}
总结
打比赛的状态很重要,每次比赛前要做好充足的准备,准备好脑细胞,比赛过程中尽量专注,调整好状态继续前进吧 。