A - Cookie Exchanges
若都为偶数且相同,显然无限轮。
否则考虑存在不同的最低位,每经过一轮之后会
−
1
-1
−1,所以就只有
l
o
g
log
log次。
#include<bits/stdc++.h>
using namespace std;
int a,b,c;
int dfs(int x,int y,int z){
if((x&1) || (y&1) || (z&1)) return 0;
return dfs((y+z)/2,(x+y)/2,(x+z)/2)+1;
}
int main(){
scanf("%d %d %d",&a,&b,&c);
if(a==b && b==c && a%2==0) printf("-1\n");
else printf("%d\n",dfs(a,b,c));
}
B - Unplanned Queries
建出询问的生成树,观察对于每一个联通块是否都满足即可。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct edge{
int y,nex;
}s[N<<1];
int first[N],len=0,n,m,fa[N];
bool tf[N],vis[N],we=true;
int findpa(int x){return fa[x]!=x?fa[x]=findpa(fa[x]):fa[x];}
void ins(int x,int y){
s[++len]=(edge){y,first[x]};first[x]=len;
}
void dfs(int x,int fa){
for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa)
dfs(s[i].y,x),tf[x]^=tf[s[i].y];
we&=(tf[x]^1);
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) fa[i]=i;
int x,y;
for(int i=1;i<=m;i++){
scanf("%d %d",&x,&y);
int fx=findpa(x),fy=findpa(y);
if(fx!=fy){
fa[fx]=fy;
ins(x,y);
}
tf[x]^=1,tf[y]^=1;
}
for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0);
printf(we?"YES\n":"NO\n");
}
C - Closed Rooms
显然移动一次之后,剩下的 k k k步都是走满的, b f s bfs bfs枚举第一次走到的位置即可。
#include<bits/stdc++.h>
using namespace std;
const int N=810;
int n,m,k,ans=1e9,st,ed;
int fx[4]={-1,1,0,0};
int fy[4]={0,0,-1,1},vis[N][N];
pair<int,int> qs[N*N];
char s[N][N];
int calc(int x,int y){
return min(min((x-1+k-1)/k,(n-x+k-1)/k),min((y-1+k-1)/k,(m-y+k-1)/k));
}
void bfs(int x,int y){
qs[st=ed=1]=make_pair(x,y);
memset(vis,-1,sizeof(vis));vis[x][y]=k;
while(st<=ed){
pair<int,int> X=qs[st++];
ans=min(ans,calc(X.first,X.second)+1);
if(!vis[X.first][X.second]) continue;
for(int i=0;i<4;i++){
int xx=X.first+fx[i],yy=X.second+fy[i];
if(xx<1 || xx>n || yy<1 || yy>m || s[xx][yy]=='#' || vis[xx][yy]!=-1) continue;
vis[xx][yy]=vis[X.first][X.second]-1;
qs[++ed]=make_pair(xx,yy);
}
}
}
int main(){
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
int x,y;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) if(s[i][j]=='S')
bfs(i,j);
printf("%d\n",ans);
}
D - Black and White Tree
先手必定操作一个叶子的邻接点,这样可以控制后手操作这个叶子,否则后手会影响更多的点,不优。
两次操作之后,直接将这两个点及其到周围点的连边删除,剩下就是个子问题。
容易发现,奇数图先手必胜。
偶数图的话,若一个点有两个或以上的奇数子树,那么先手必胜。其他情况后手必胜。
两个奇数子树,可以拿一个奇数子树出来与根节点相接,形成一个偶数子树,操作完之后就剩下一个单独的奇数联通块,操作它就先手必胜。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct edge{
int y,nex;
}s[N<<1];
int first[N],len=0,n,sz[N];
bool tf=false;
void ins(int x,int y){
s[++len]=(edge){y,first[x]};first[x]=len;
}
void dfs(int x,int fa){
sz[x]=1;
int tot=0;
for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa){
dfs(s[i].y,x),sz[x]+=sz[s[i].y];
if(sz[s[i].y]&1) tot++;
}
if((n-sz[x])&1) tot++;
if(tot>=2) tf=true;
}
int main(){
scanf("%d",&n);
int x,y;
for(int i=1;i<n;i++){
scanf("%d %d",&x,&y);
ins(x,y),ins(y,x);
}
dfs(1,0);
printf(tf?"First\n":"Second\n");
}
E - Blue and Red Tree
树链剖分板子题。
考虑一次操作之后分为两个互不相关的联通块。
将
c
i
,
d
i
c_i,d_i
ci,di路径加
1
1
1,之后每次找出一条仅被覆盖一次的边,并将覆盖它的删除即可。
这样操作肯定没有问题,否则无解。
具体的话线段树上使用
v
e
c
t
o
r
vector
vector来维护,维护区间最小值,支持区间
+
1
,
−
1
+1,-1
+1,−1,删除的时候将
log
\log
log个点的
v
e
c
t
o
r
vector
vector清空就可以保证时间复杂度,时间复杂度正确
O
(
n
log
2
n
)
O(n\log^2 n)
O(nlog2n)。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct edge{
int y,nex;
}s[N<<1];
int n,first[N],len=0,fa[N],sz[N],son[N],top[N],dfn[N],tim,dep[N];
bool tf[N];
pair<int,int> p[N];
int mmin[300010],tag[300010];
vector<int> V[300010];
void ins(int x,int y){
s[++len]=(edge){y,first[x]};first[x]=len;
}
void dfs_1(int x){
sz[x]=1;
for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa[x]){
fa[s[i].y]=x;dep[s[i].y]=dep[x]+1;
dfs_1(s[i].y);
sz[x]+=sz[s[i].y];
if(sz[s[i].y]>sz[son[x]]) son[x]=s[i].y;
}
}
void dfs_2(int x,int tp){
dfn[x]=++tim;top[x]=tp;
if(son[x]) dfs_2(son[x],tp);
for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa[x] && s[i].y!=son[x])
dfs_2(s[i].y,s[i].y);
}
#define ls now<<1
#define rs now<<1|1
void psd(int now){
if(tag[now]){
tag[ls]+=tag[now];
tag[rs]+=tag[now];
mmin[ls]+=tag[now];
mmin[rs]+=tag[now];
tag[now]=0;
}
}
int X,num;
void adt(int now,int x,int y,int l=2,int r=n){
if(x==l && y==r){
V[now].push_back(X);
tag[now]++;mmin[now]++;
return ;
}
psd(now);
int mid=(l+r)/2;
if(y<=mid) adt(ls,x,y,l,mid);
else if(mid<x) adt(rs,x,y,mid+1,r);
else adt(ls,x,mid,l,mid),adt(rs,mid+1,y,mid+1,r);
mmin[now]=min(mmin[ls],mmin[rs]);
}
void dt(int now,int x,int y,int l=2,int r=n){
if(x==l && y==r){tag[now]--;mmin[now]--;return ;}
psd(now);
int mid=(l+r)/2;
if(y<=mid) dt(ls,x,y,l,mid);
else if(mid<x) dt(rs,x,y,mid+1,r);
else dt(ls,x,mid,l,mid),dt(rs,mid+1,y,mid+1,r);
mmin[now]=min(mmin[ls],mmin[rs]);
}
void gs(int now,int l=2,int r=n){
if(mmin[now]!=1) {num=-1;return ;}
for(int i=0;i<V[now].size();i++) if(!tf[V[now][i]]){
num=V[now][i];
break;
}
V[now].resize(0);
if(l==r) {mmin[now]=1e9;return ;}
int mid=(l+r)/2;
psd(now);
if(mmin[ls]==1) gs(ls,l,mid);
else gs(rs,mid+1,r);
mmin[now]=min(mmin[ls],mmin[rs]);
}
void solve(int x,int y){
while(top[x]!=top[y]){
if(dep[top[y]]>=dep[top[x]]) swap(x,y);
adt(1,dfn[top[x]],dfn[x]);x=fa[top[x]];
}
if(dep[x]>=dep[y]) swap(x,y);
if(x!=y) adt(1,dfn[x]+1,dfn[y]);
}
void dsolve(int x,int y){
while(top[x]!=top[y]){
if(dep[top[y]]>=dep[top[x]]) swap(x,y);
dt(1,dfn[top[x]],dfn[x]);x=fa[top[x]];
}
if(dep[x]>=dep[y]) swap(x,y);
if(x!=y) dt(1,dfn[x]+1,dfn[y]);
}
int main(){
scanf("%d",&n);
int x,y;
for(int i=1;i<n;i++){
scanf("%d %d",&x,&y);
ins(x,y),ins(y,x);
}
dfs_1(1);dfs_2(1,1);
for(int i=1;i<n;i++){
scanf("%d %d",&p[i].first,&p[i].second);
X=i;solve(p[i].first,p[i].second);
}
for(int i=1;i<n;i++){
gs(1);
if(num==-1){printf("NO\n");return 0;}
tf[num]=1;
dsolve(p[num].first,p[num].second);
}
printf("YES\n");
}
F - Strange Sorting
前面的题好简单,最后一题好难。
首先考虑增量法,假设已经处理出值为
[
2
,
n
]
[2,n]
[2,n]的答案,现在要求值为
[
1
,
n
]
[1,n]
[1,n]的答案。
若
a
n
s
[
i
+
1
]
=
0
ans[i+1]=0
ans[i+1]=0 ,若
1
1
1在最前面,那么
a
n
s
[
i
]
=
0
ans[i]=0
ans[i]=0,否则
a
n
s
[
i
]
=
1
ans[i]=1
ans[i]=1。
否则,我们就来玩一玩。
首先证明一个小引理:
2
2
2在
a
n
s
[
i
+
1
]
−
1
ans[i+1]-1
ans[i+1]−1次操作后一定不在
[
2
,
n
]
[2,n]
[2,n]中的第一个。
考虑反证法即可,若
2
2
2在第一个那么必然是
high
\text{high}
high,而必定存在
low
\text{low}
low点,
2
2
2在
1
1
1此操作后必定不在首位,矛盾。
假设在第一位的这个元素为
x
x
x。
再证明一个小引理:
x
x
x为
high
\text{high}
high当且仅当
x
x
x在
[
1
,
n
]
[1,n]
[1,n]中的第一个位置。
考虑反证法即可,如果
x
x
x为
high
\text{high}
high且不在第一个位置。
前面就存在另一个
high
\text{high}
high,一次操作之后,
x
x
x会排在这个
high
\text{high}
high的后面。
如果两者还是相同类型的的,
x
x
x会一直跟在他后面。
如果
x
x
x变成
high
\text{high}
high了,就有新的一个前面的值换着跟。
在有限次的操作之后,
x
x
x永远都到不了第一个,矛盾。
可以发现,在
a
n
s
[
i
+
1
]
−
1
ans[i+1]-1
ans[i+1]−1次操作后,如果
x
,
1
,
2
x,1,2
x,1,2三者的排列是
(
x
,
1
,
2
)
(x,1,2)
(x,1,2),那么
a
n
s
[
i
]
=
a
n
s
[
i
+
1
]
ans[i]=ans[i+1]
ans[i]=ans[i+1],否则
a
n
s
[
i
]
=
a
n
s
[
i
+
1
]
+
1
ans[i]=ans[i+1]+1
ans[i]=ans[i+1]+1。
再来证明一个定理:
x
,
1
,
2
x,1,2
x,1,2三者的循环关系一次排序后中永远不变。
循环关系指的是
(
1
,
2
,
3
)
=
(
2
,
3
,
1
)
=
(
3
,
1
,
2
)
(1,2,3)=(2,3,1)=(3,1,2)
(1,2,3)=(2,3,1)=(3,1,2)。
证明可以这样考虑:
首先如果
x
x
x不在首尾必为
low
\text{low}
low。
若为
(
x
,
1
,
2
)
(x,1,2)
(x,1,2)或
(
x
,
2
,
1
)
(x,2,1)
(x,2,1)
若
x
x
x为
low
\text{low}
low,那么
1
,
2
1,2
1,2也为
low
\text{low}
low,不变。
若
x
x
x为
high
\text{high}
high,那么
1
,
2
1,2
1,2为
low
\text{low}
low,转变为
(
1
,
2
,
x
)
(1,2,x)
(1,2,x)或
(
2
,
1
,
x
)
(2,1,x)
(2,1,x),循环关系不变。
若为
(
2
,
x
,
1
)
(2,x,1)
(2,x,1)或
(
2
,
1
,
x
)
(2,1,x)
(2,1,x)
若
2
2
2为
low
\text{low}
low,那么
1
,
x
1,x
1,x也为
low
\text{low}
low。
若
2
2
2为
high
\text{high}
high,那么
1
,
x
1,x
1,x为
low
\text{low}
low,转变为
(
x
,
1
,
2
)
(x,1,2)
(x,1,2)或
(
1
,
x
,
2
)
(1,x,2)
(1,x,2),循环关系不变。
若为
(
1
,
x
,
2
)
(1,x,2)
(1,x,2)
若
1
1
1为
low
\text{low}
low,那么
2
,
x
2,x
2,x为
low
\text{low}
low。
若
1
1
1为
high
\text{high}
high,
2
,
x
2,x
2,x为
low
\text{low}
low,转变为
(
x
,
2
,
1
)
(x,2,1)
(x,2,1),循环关系不变。
若为
(
1
,
2
,
x
)
(1,2,x)
(1,2,x)
若
1
1
1为
low
\text{low}
low,那么
2
,
x
2,x
2,x为
low
\text{low}
low。
若
1
,
2
1,2
1,2为
high
\text{high}
high,
x
x
x为
low
\text{low}
low,转变为
(
x
,
1
,
2
)
(x,1,2)
(x,1,2),循环关系不变。
若
1
1
1为
high
\text{high}
high,
2
2
2为
low
\text{low}
low,
x
x
x为
low
\text{low}
low,转变为
(
x
,
1
,
2
)
(x,1,2)
(x,1,2),循环关系不变。
最后记录一下
a
n
s
ans
ans和每一轮的
x
x
x,
D
p
Dp
Dp即可。
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int n,a[N],pos[N],p[N],ans[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),pos[a[i]]=i;
for(int i=n-1;i>=1;i--){
ans[i]=ans[i+1];
if(ans[i]==0){
if(pos[i]>pos[i+1])
ans[i]++,p[i]=i+1;
}
else {
if(pos[p[i+1]]<pos[i] && pos[i]<pos[i+1] ||
pos[i]<pos[i+1] && pos[i+1]<pos[p[i+1]] ||
pos[i+1]<pos[p[i+1]] && pos[p[i+1]]<pos[i]) p[i]=p[i+1];
else{
ans[i]++;
p[i]=i+1;
}
}
}
printf("%d\n",ans[1]);
}