Codeforces Round #571 (Div. 2)
话说等了差不多一年,我终于有了一点时间上CF。。。然而。。。
A. Vus the Cossack and a Contest
题目大意
有 N N N个人,两种奖品,每种奖品分别有 A A A个, B B B个。问每个人能否都能得到两种奖品,且每种奖品至少有一个。
分析
emmm…简单的if
语句的运用,直接写就是了。
参考代码
#include<cstdio>
#include<algorithm>
using namespace std;
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int n,a,b;
scanf("%d %d %d",&n,&a,&b);
if(a<n||b<n)
puts("No");
else puts("Yes");
return 0;
}
B.Vus the Cossack and a Game
(此题已被删除)
题目大意
给定一个 N , M N,M N,M表示一个 N × M N\times M N×M的方格,要求放上一些 1 × 2 1\times2 1×2的方块,使得每个方块周围一圈没有其他方块,且每个方块的每个角不相对。
举个例子:不合法的状态:
合法的状态:
问在这个
N
×
M
N\times M
N×M的方格上可以放多少个
1
×
2
1\times2
1×2的方块。
分析
这道题是个结论题,答案就是 ⌊ ( N + 1 ) ( M + 1 ) 6 ⌋ \lfloor\frac{(N+1)(M+1)}{6}\rfloor ⌊6(N+1)(M+1)⌋,具体证明点这里
参考代码
我觉得没这必要吧
C. Vus the Cossack and Strings
题目大意
定义 f ( b , c ) f(b,c) f(b,c)为两串不相同字符的数量。给定两个01串 s 1 , s 2 s_1,s_2 s1,s2,保证 ∣ s 2 ∣ ≤ ∣ s 1 ∣ |s_2|\le|s_1| ∣s2∣≤∣s1∣,将 s 1 s_1 s1中长度为 ∣ s 2 ∣ |s_2| ∣s2∣的子串取出,与 s 2 s_2 s2相比较并求得此时的 f f f,问总共有多少个 f f f值是偶数。
分析
我们显然不能 O ( N 2 ) O(N^2) O(N2)暴力比较。
由于这是一个01串,所以我们考虑在 s 2 s_2 s2向右移动一位后,它对应位置上的变化情况。
由于这是一个01串,所以只要 s 1 s_1 s1的第 i i i个位置和 s 2 s_2 s2的第 j j j个位置相同, s 1 s_1 s1的第 i i i个位置和第 i + 1 i+1 i+1个位置不同,那么 s 2 s_2 s2向右平移后, j j j位置对上了 s 1 s_1 s1的 i + 1 i+1 i+1位置,显然我们可以发现这个函数值由于这个位置的变化应加上1。
所以我们只需预处理出 s 1 s_1 s1中不相同的位置的个数前缀和,并比较即可。
参考代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxn=1e6;
char a[Maxn+5],b[Maxn+5];
int s[Maxn+5];
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%s\n%s",a+1,b+1);
int lena=strlen(a+1),lenb=strlen(b+1);
for(int i=1;i<lena;i++)
s[i]=s[i-1]+(a[i]!=a[i+1]);
int t=0,ans=0;
for(int i=1;i<=lenb;i++)
if(a[i]!=b[i])
t++;
if(t%2==0)
ans++;
for(int i=0;i<lena-lenb;i++) {
if((s[i+lenb]-s[i])%2==1)
t++;
if(t%2==0)ans++;
}
printf("%d\n",ans);
return 0;
}
D. Vus the Cossack and Numbers
题目大意
给定有 N N N个数的实数,保证它们的和为 0 0 0,现要求将这些数向上或向下取整,要求取整后和仍然为 0 0 0,输出任意一种方案。
分析
我们可以把所有数向0取整(利用double
转int
的特性)。
然后求一遍和,这个和有三种情况:
- 为 0 0 0,直接输出即可;
- 大于 0 0 0,我们选出这么多的数,并将它们全部减去 1 1 1即可;
- 小于 0 0 0,做法同大于 0 0 0的情况。
也可以用floor
函数全部向下取整,这样所有的和肯定是小于等于
0
0
0,那么只有一种情况出现。
参考代码
#include<map>
#include<cstdio>
#include<algorithm>
using namespace std;
const int Maxn=1e5;
int N;
double A[Maxn+5];
int t[Maxn+5];
double Abs(double x) {
if(x<0)return -x;
return x;
}
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d",&N);
int sum=0;
for(int i=1;i<=N;i++) {
scanf("%lf",&A[i]);
t[i]=A[i];
sum+=t[i];
}
if(sum>0) {
for(int i=1;i<=N;i++) {
if(Abs(1.0*t[i]-1-A[i])<1.0)
t[i]--,sum--;
if(sum==0)break;
}
} else if(sum<0) {
for(int i=1;i<=N;i++) {
if(Abs(1.0*t[i]+1-A[i])<1.0)
t[i]++,sum++;
if(sum==0)break;
}
}
for(int i=1;i<=N;i++)
printf("%d\n",t[i]);
return 0;
}
E. Vus the Cossack and a Field
题目大意
给定一个 N × M N\times M N×M初始01矩阵,按照如下步骤变换:
- 将这个矩阵取反;
- 将取反后矩阵摆到原矩阵的右方和下方;
- 将原矩阵复制一个放在原矩阵的右下方;
- 重复这个操作无限次。
再给出 q q q个询问,每个询问表示以 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)为左上角, ( x 2 , y 2 ) (x_2,y_2) (x2,y2)为右下角的矩形中 1 1 1的个数。
分析
这不是分治题,这是规律题!!!
我们记 f ( x , y ) f(x,y) f(x,y)为以 ( 1 , 1 ) (1,1) (1,1)为左上角, ( x , y ) (x,y) (x,y)为右下角的矩阵的 1 1 1的个数。
则原问题可转化为求 f ( x 2 , y 2 ) − f ( x 1 − 1 , y 2 ) − f ( x 2 , y 1 − 1 ) + f ( x 1 − 1 , y 1 − 1 ) f(x_2,y_2)-f(x_1-1,y_2)-f(x_2,y_1-1)+f(x_1-1,y_1-1) f(x2,y2)−f(x1−1,y2)−f(x2,y1−1)+f(x1−1,y1−1)
的值,所以我们只需设计出 f ( x , y ) f(x,y) f(x,y)的求法即可。
我们先考虑原始矩阵只有一个元素的情况。
当未做任何变换时: 1 1 1
做了一次变换: 1 0 0 1 1\ 0\\0\ 1 1 00 1
做了两次变换: 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1 1\ 0\ 0\ 1\\0\ 1\ 1\ 0\\0\ 1\ 1\ 0\\1\ 0\ 0\ 1 1 0 0 10 1 1 00 1 1 01 0 0 1
做三次: 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1 0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1 1\ 0\ 0\ 1\ 0\ 1\ 1\ 0\\0\ 1\ 1\ 0\ 1\ 0\ 0\ 1\\0\ 1\ 1\ 0\ 1\ 0\ 0\ 1\\1\ 0\ 0\ 1\ 0\ 1\ 1\ 0\\0\ 1\ 1\ 0\ 1\ 0\ 0\ 1\\1\ 0\ 0\ 1\ 0\ 1\ 1\ 0\\1\ 0 \ 0\ 1\ 0\ 1\ 1\ 0\\0\ 1\ 1\ 0\ 1\ 0\ 0\ 1 1 0 0 1 0 1 1 00 1 1 0 1 0 0 10 1 1 0 1 0 0 11 0 0 1 0 1 1 00 1 1 0 1 0 0 11 0 0 1 0 1 1 01 0 0 1 0 1 1 00 1 1 0 1 0 0 1
不难发现,相邻的奇数行和偶数行、奇数列与偶数列,都能抵消为1个 1 1 1。
那么扩展到 N × M N\times M N×M的方格呢?
很显然由于取反,矩阵中原来为 1 1 1的位置变成了 0 0 0,原来为 0 0 0的地方变成了 1 1 1,所以这样相邻的矩阵仍能抵消,只是这是 N × M N\times M N×M个 1 1 1了。
对于不满 N × M N\times M N×M的矩阵,只要残余部分是相同的,仍有这种规律。
说了这么多,接下来进入正题:求 f ( x , y ) f(x,y) f(x,y)。
仍以 1 1 1代替 N × M N\times M N×M的矩阵。
我们定义 f x = ⌊ x − 1 N ⌋ + 1 , f y = ⌊ y − 1 M ⌋ + 1 f_x=\lfloor\frac{x-1}{N}\rfloor+1,f_y=\lfloor\frac{y-1}{M}\rfloor+1 fx=⌊Nx−1⌋+1,fy=⌊My−1⌋+1。
所以结论就分为四种情况讨论:
- f x , f y f_x,f_y fx,fy均为偶数;
- f x , f y f_x,f_y fx,fy均为奇数;
- f x f_x fx为偶数, f y f_y fy为奇数;
- f x f_x fx为奇数, f y f_y fy为偶数。
这四种情况的讨论是差不多的,所以接下来我们只以第一种情况为例。
如下图:
黄色的点是我们得到的
(
x
,
y
)
(x,y)
(x,y)
我们很容易推知红色部分答案为 ( ( f x − 1 ) ( f y − 1 ) − 1 ) N M 2 \frac{((f_x-1)(f_y-1)-1)NM}{2} 2((fx−1)(fy−1)−1)NM
同理可知紫色,褐色,蓝色部分的答案,将它们加起来就可以了。
那么如何判断一个点所在的位置是否是取反的矩阵?
我们定义 B i t C o u n t ( x ) BitCount(x) BitCount(x)为 x x x二进制表示中的 1 1 1的个数,那么当 B i t C o u n t ( b ) + B i t C o u n t ( a ) BitCount(b)+BitCount(a) BitCount(b)+BitCount(a)( ( a , b ) (a,b) (a,b)为该矩阵在整个矩阵中的坐标)是奇数时,则该矩阵是取反的。
我也不知道怎么证这玩意。。。
参考代码
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int Maxn=1000;
int N,M;
char s[Maxn+5][Maxn+5];
ll f[Maxn+5][Maxn+5];
ll Normal(ll x,ll y) {return f[x][y];}
ll Inverse(ll x,ll y) {return (x+1)*(y+1)-f[x][y];}
int BitCount(ll x) {
int ret=0;
while(x)
ret++,x-=(x&(-x));
return ret;
}
ll Solve(ll x,ll y) {
if(x<0||y<0)return 0;
ll fx=x/N+1,fy=y/M+1;
ll ret=0;
if(fx%2==0&&fy%2==0) {
ret+=(((fx-1)*(fy-1)-1)/2*N*M);
x%=N,y%=M;
ret+=((fx-2)/2*(y+1)*N);
ret+=((fy-2)/2*(x+1)*M);
if((BitCount(fx-2)+BitCount(fy-2))%2)
ret+=Inverse(N-1,M-1);
else ret+=Normal(N-1,M-1);
if((BitCount(fx-1)+BitCount(fy-1))%2)
ret+=Inverse(x,y)+Normal(x,M-1)+Normal(N-1,y);
else ret+=Normal(x,y)+Inverse(x,M-1)+Inverse(N-1,y);
} else if(fx%2&&fy%2) {
ret+=((fx-1)/2*(fy-1)*N*M);
x%=N,y%=M;
ret+=((fx-1)/2*(y+1)*N);
ret+=((fy-1)/2*(x+1)*M);
if((BitCount(fx-1)+BitCount(fy-1))%2)
ret+=Inverse(x,y);
else ret+=Normal(x,y);
} else if(fx%2==0&&fy%2) {
ret+=((fx-1)*(fy-1)/2*N*M);
x%=N,y%=M;
ret+=((fx-2)/2*(y+1)*N);
ret+=((fy-1)/2*(x+1)*M);
if((BitCount(fx-1)+BitCount(fy-1))%2)
ret+=Inverse(x,y)+Normal(N-1,y);
else ret+=Normal(x,y)+Inverse(N-1,y);
} else if(fx%2&&fy%2==0) {
ret+=((fx-1)/2*(fy-1)*N*M);
x%=N,y%=M;
ret+=((fx-1)/2*(y+1)*N);
ret+=((fy-2)/2*(x+1)*M);
if((BitCount(fx-1)+BitCount(fy-1))%2)
ret+=Inverse(x,y)+Normal(x,M-1);
else ret+=Normal(x,y)+Inverse(x,M-1);
}
return ret;
}
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int q;
scanf("%d %d %d",&N,&M,&q);
for(int i=0;i<N;i++) {
scanf("%s",s[i]);
for(int j=0;j<M;j++)
f[i][j]=s[i][j]-'0';
}
for(int i=1;i<N;i++)
f[i][0]+=f[i-1][0];
for(int j=1;j<M;j++)
f[0][j]+=f[0][j-1];
for(int i=1;i<N;i++)
for(int j=1;j<M;j++)
f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
while(q--) {
ll x1,y1,x2,y2;
scanf("%lld %lld %lld %lld",&x1,&y1,&x2,&y2);
x1--,x2--,y1--,y2--;
ll ans=Solve(x2,y2)-Solve(x1-1,y2)-Solve(x2,y1-1)+Solve(x1-1,y1-1);
printf("%lld\n",ans);
}
return 0;
}
F. Vus the Cossack and a Graph
这题STL要爆???惊了(好吧我就这样炸了5次。。。)
题目大意
给定一个有 N N N个节点和 M M M条边的图,记第 i i i个点的度为 d i d_i di。现要求删除一些边,使得剩余边的数量不大于 ⌈ N + M 2 ⌉ \lceil\frac{N+M}{2}\rceil ⌈2N+M⌉,且第 i i i个点删后的度数不小于 ⌈ d i 2 ⌉ \lceil\frac{d_i}{2}\rceil ⌈2di⌉。
分析
我们创建一个虚拟节点即 0 0 0号节点,并把所有度数为奇数的点全部和它相连。很容易通过所有顶点的度数之和等于边数来证明 0 0 0号节点的度数是一个偶数。
由于最多只有不超过 N N N个点的度数为奇数,所以,我们显然可以证明,新的边数 k ≤ N + M k\le N+M k≤N+M。
因为所有的点的度数为偶数,所以这个图存在欧拉回路。考虑做欧拉回路,记按欧拉回路次序找到的边为 e 1 , e 2 , e 3 , … , e k e_1,e_2,e_3,\ldots,e_k e1,e2,e3,…,ek,接下来我们删掉编号为偶数的边,这样我们所得到的就只有 ⌈ k 2 ⌉ \lceil\frac{k}{2}\rceil ⌈2k⌉条了。易证到 ⌈ k 2 ⌉ ≤ ⌈ N + M 2 ⌉ \lceil\frac{k}{2}\rceil\le\lceil\frac{N+M}{2}\rceil ⌈2k⌉≤⌈2N+M⌉。
我们应按照先删虚边,再删实边的方法来删边:
- 若该边是虚边,则直接删掉;
- 若该边是实边,由于我们只把奇数度数的点连了虚边,所以我们又分为两种情况讨论:
- 若该边连着一个度数为奇数的点,我们可以发现它的两边有至少一条虚边,我们将这条虚边删去即可;
- 若该边连着的两个点均为度数为偶数的点,那么我们只能强行删掉这条边了(这条边的两边都不是虚边)。
我们将所有未被删的实边输出即可。
注意由于题目未保证所有点形成一个连通块,所以我们必须对每个点做一次欧拉回路。
参考代码
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int Maxn=1e6;
int N,M,totm;
int d[Maxn+5];
struct Node {
int to;
int id;
};
vector<Node> G[Maxn+5];
struct Edge {
int u,v;
int typ;
}e[Maxn*2+5];
void addedge(int u,int v,int typ) {
G[u].push_back(Node{v,++totm});
G[v].push_back(Node{u,totm});
e[totm]=Edge{u,v,typ};
}
bool vis[Maxn*2+5],del[Maxn*2+5];
int tmp[Maxn*2+5],cnt;
int S[Maxn+5];
int stk[Maxn*2+5],ed[Maxn*2+5],top;
void EulerPath(int s) {
cnt=0,stk[++top]=s;
while(top>0) {
int u=stk[top],i;
for(i=S[u];i<G[u].size();i++)
if(!vis[G[u][i].id])
break;
if(i<G[u].size()) {
stk[++top]=G[u][i].to;
ed[top]=G[u][i].id;
vis[G[u][i].id]=true;
S[u]=i+1;
} else tmp[++cnt]=ed[top--];
}
}
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d %d",&N,&M);
for(int i=1;i<=M;i++) {
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v,1);
d[u]++,d[v]++;
}
for(int i=1;i<=N;i++)
if(d[i]%2)
addedge(0,i,0);
int num=M;
for(int i=0;i<=N;i++) {
EulerPath(i);
for(int j=2;j<=cnt;j+=2) {
int id=tmp[j];
if(e[id].typ==0)
del[id]=true;
else {
int t=tmp[j-1];
if(e[t].typ==0&&del[t]==false) {
del[t]=true;
continue;
}
t=tmp[j%cnt+1];
if(e[t].typ==0&&del[t]==false) {
del[t]=true;
continue;
}
del[id]=true;
num--;
}
}
}
printf("%d\n",num);
for(int i=1;i<=totm;i++)
if(del[i]==false&&e[i].typ==1)
printf("%d %d\n",e[i].u,e[i].v);
return 0;
}