Codeforces Global Round 14[CF1515]

题目链接

CF

题解

A. Phoenix and Gold

因为每个数不相等,从小到大排序,遇到相等的,看是否能和后面的交换。不能交换则一定不行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())
int n,x,a[29349];
int main()
{
CASET
{
n=read(); x=read();
fo(i,1,n) a[i]=read();
sort(a+1,a+n+1);
int sum=0;
fo(i,1,n) sum+=a[i];
if(sum==x) {puts("NO"); continue;}
else if(sum<x)
{
puts("YES");
fo(i,1,n) printf("%d ",a[i]); printf("\n");
}
else
{
int tmp=0;
fo(i,1,n)
{
tmp+=a[i];
if(tmp==x) {swap(a[i],a[i+1]); break;}
}
puts("YES");
fo(i,1,n) printf("%d ",a[i]); printf("\n");
}

}
return 0;
}

B. Phoenix and Puzzle

考虑直角边和斜边形成的正方形,即假设其直角边长为 $1$,则正方形个数 $n$ 要满足:

$\frac{n}{2}=x^2$ 或 $\frac{n}{2}=(\sqrt{2}x)^2$。

随便判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())

inline bool check(int x)
{
int y=sqrt(x);
return y*y==x;
}
int n;
int main()
{
CASET
{
n=read();
if(n%2==0&&check(n/2)) puts("YES");
else if(n%4==0&&check(n/4)) puts("YES");
else puts("NO");
}
return 0;
}

C. Phoenix and Towers

由于 $1\le h_i\le x$,那么每次选当前最小的塔就可以保证他们的差不超过 $x$ 了。

用一个堆维护即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())
struct node{
int id; ll x;
friend inline bool operator<(const node &A,const node &B)
{
return A.x==B.x?A.id<B.id:A.x>B.x;
}
};
priority_queue<node> q;
int n,m,x,ans[100005];
int main()
{
CASET
{
n=read(); m=read(); x=read();
fo(i,1,m) q.push((node){i,0});
fo(i,1,n)
{
x=read();
node now=q.top(); q.pop();
ans[i]=now.id;
now.x+=x;
q.push(now);
}
puts("YES");
fo(i,1,n) printf("%d ",ans[i]);
printf("\n");
fo(i,1,m) q.pop();
}
return 0;
}

D. Phoenix and Socks

显然,相同颜色的先进行配对。

接下来,不妨设配对完后剩下的左袜子比较多。

那么就先用左袜子变成右袜子与相同颜色的配对。注意不能使左袜子比右袜子少了。

剩下的随便做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())
const int N=4e5+5;
int n,l,r;
int le[N],ri[N],a[N];
int main()
{
CASET
{
n=read(); l=read(); r=read();
fo(i,1,n) le[i]=ri[i]=0;
fo(i,1,l) le[a[i]=read()]++;
fo(i,1,r) ri[a[i+l]=read()]++;
int ans=0;
fo(i,1,n)
{
int tmp=min(le[i],ri[i]);
le[i]-=tmp; ri[i]-=tmp;
}
int t1=0,t2=0;
fo(i,1,n) t1+=le[i],t2+=ri[i];
if(t1>=t2)
{
fo(i,1,l)
if(le[a[i]]>=2&&t1>t2)
{
le[a[i]]-=2;
ans++;
t1-=2;
}
ans+=(t1-t2)/2+(t1+t2)/2;
}
else
{
fo(i,l+1,n)
if(ri[a[i]]>=2&&t2>t1)
{
ri[a[i]]-=2;
ans++;
t2-=2;
}
ans+=(t2-t1)/2+(t1+t2)/2;
}
printf("%d\n",ans);
}
return 0;
}

E. Phoenix and Computers

这个连续段DP的思路有点妙。

我们设 $f_{i,j}$ 表示已经填了 $i$ 个数,且共有 $j$ 段距离>1的连续段,每相邻两段的距离不确定时的方案数。

转移分5种情况讨论即可。很好写。

时间复杂度 $O(n^2)$。

也可以像题解那样,算出不自动打开的方案数,然后设 $f_{i,j}$ 为打开前 $i$ 个数,且自动打开 $j$ 个的方案数。但这样的复杂度就是 $O(n^3)$ 的了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())
const int N=505;
ll mod;
inline void add(ll &x,ll y){x+=y; x=(x<mod)?x:x-mod;}

ll f[N][N];
int n;
int main()
{
n=read(); mod=read();
f[1][1]=1;
ll tmp;
fo(i,1,n-1)
fo(j,1,n-1)
{
tmp=f[i][j];
if(i+1+2*j<=n) add(f[i+1][j+1],tmp*(j+1)%mod);
if(i+1+2*(j-1)<=n) add(f[i+1][j],tmp*2*j%mod);
if(i+2+2*(j-1)<=n) add(f[i+2][j],tmp*2*j%mod);
if(j>=2&&i+2+2*(j-2)<=n) add(f[i+2][j-1],tmp*2*(j-1)%mod);
if(j>=2&&i+3+2*(j-2)<=n) add(f[i+3][j-1],tmp*(j-1)%mod);
}
printf("%d\n",f[n][1]);
return 0;
}

F. Phoenix and Earthquake

有一个十分牛逼的性质:任意一棵生成树,若 $\sum_{i=1}^na_i\geq (n-1)x$,那么一定可以。

考虑构造,对于某个子树 $u$ 以及其儿子 $v$,如果 $a_u+a_v>x$,那么直接操作。否则等后面再操作。若 $a_u+a_v\le x$,那么 $a_v\le x$,那么 $\sum_{i=1}^na_i-a_v\geq (n-2)x$,据归纳法可知,删掉 $v$ 以后还存在一个解。

然后一个dfs就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())
const int N=6e5+5;
ll a[N];
int n,m,x,ans[N],l,r;
int ver[N],head[N],val[N],ne[N],tot=1;
inline void add(int x,int y,int z)
{
ver[++tot]=y; val[tot]=z; ne[tot]=head[x]; head[x]=tot;
ver[++tot]=x; val[tot]=z; ne[tot]=head[y]; head[y]=tot;
}
bool vis[N];
void dfs(int u,int pre,int now)
{
vis[u]=1;
for(int i=head[u],v;i;i=ne[i])
{
v=ver[i];
if(vis[v]) continue;
dfs(v,u,val[i]);
}
if(!pre) return;
if(a[u]>=x)
{
ans[++l]=now;
a[pre]+=a[u]-x;
}
else
{
ans[r--]=now;
}
}

int main()
{
ll sum=0;
n=read(); m=read(); x=read();
sum=-1ll*(n-1)*x;
fo(i,1,n) a[i]=read(),sum+=a[i];
if(sum<0) {puts("NO"); return 0;}
fo(i,1,m) add(read(),read(),i);
puts("YES");
l=0; r=n-1;
dfs(1,0,-1);
fo(i,1,n-1) printf("%d\n",ans[i]);
return 0;
}

G. Phoenix and Odometers

显然,$a$ 能走的点必在 $a$ 在内的强联通分量里。

而如果存在从 $u$ 走到 $v$ 的距离为 $l\pmod p$ 的路径,那么必存在 $v$ 到 $u$ 的距离为 $-l\pmod p$ 的路径,因为存在经过 $u,v$ 的环,从 $v$ 开始走这个环 $p-1$ 次,再走到 $u$ ,就可以了。

而显然,如果有两个环,环长分别为 $a,b$,那么 $\gcd(a,b)$ 能走出来。

那么算出来 $a$ 所在强联通分量所有环的环长的 $\gcd$ 即可。

在Kosaraju过程中,记录点到根节点距离,可以算出来。

时间复杂度 $O(n\log m)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())
const int N=2e5+5;
struct edge{
int y,v;
};
vector<edge> adj[N],adk[N];
vector<int> dfn;
bool vis[N];
ll f[N],g[N];
int bel[N];
void dfs1(int u,int pre)
{
vis[u]=1;
for(auto e:adj[u])
if(!vis[e.y])
dfs1(e.y,u);
dfn.pb(u);
}
void dfs2(int u,int pre)
{
vis[u]=1;
for(auto e:adk[u])
if(!vis[e.y])
{
f[e.y]=f[u]+e.v;
bel[e.y]=bel[u];
dfs2(e.y,u);
}
else if(bel[e.y]==bel[u])
g[bel[u]]=__gcd(g[bel[u]],f[u]+e.v-f[e.y]);
}

int n,m,cnt;
int main()
{
n=read(); m=read();
fo(i,1,m)
{
int x,y,z;
x=read(); y=read(); z=read();
adj[x].pb((edge){y,z});
adk[y].pb((edge){x,z});
}
fo(i,1,n) if(!vis[i]) dfs1(i,0);
fo(i,1,n) vis[i]=0;
reverse(all(dfn));
for(int i:dfn)
if(!vis[i])
{
bel[i]=++cnt;
dfs2(i,0);
}
ll a,s,x;
CASET
{
a=bel[read()]; s=read(); x=read();
s=(x-s)%x;
x=__gcd(x,g[a]);
puts((s%x==0)?"YES":"NO");
}
return 0;
}

H. Phoenix and Bits

And操作可以看做是Xor和Or操作的结合。于是我们只需要考虑Xor和Or操作即可。

Xor操作直接在Trie里打标记。

Or操作发现有时候会合并一些节点的左右儿子,有时候相当于打Xor标记。并且,合并左右儿子的时间复杂度不超过 $log^2n$ 。

如何判断什么时候打Xor标记呢?当且仅当Or操作中的数只存在某些位置为 $1$,且该节点中的数在这一位上均为 $0$。

那么记一下节点内所有值的Or,以及取反后的Or就好了。

对于区间操作,我们可以类似平衡树和线段树分裂的操作就可以了。

时间复杂度 $O(n\log^2m)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())

const int M=1.3e7;
const int W=(1<<20)-1;
int ch[M][2],s[M],ta[M],to[M],tx[M],rt,cnt;
inline void pushup(int u)
{
if(!u) return;
s[u]=s[ch[u][0]]+s[ch[u][1]];
ta[u]=ta[ch[u][0]]|ta[ch[u][1]];
to[u]=to[ch[u][0]]|to[ch[u][1]];
}
inline void pushxor(int u,int dep,int t)
{
if(!u) return;
if((t>>dep)&1) swap(ch[u][0],ch[u][1]);
tx[u]^=t;
int tta=ta[u],tto=to[u];
to[u]=(tto&(W^t))|(tta&t);
ta[u]=(tta&(W^t))|(tto&t);
}
inline void pushdown(int u,int dep)
{
if(!u||!tx[u]) return;
pushxor(ch[u][0],dep-1,tx[u]);
pushxor(ch[u][1],dep-1,tx[u]);
tx[u]=0;
}
void ins(int &u,int dep,int t)
{
if(!u) u=++cnt;
if(dep<0)
{
s[u]=1;
tx[u]=0;
to[u]=t;
ta[u]=W^t;
return;
}
int c=(t>>dep)&1;
ins(ch[u][c],dep-1,t);
pushup(u);
}
void split(int &x,int &y,int dep,int l,int r,int L,int R)
{
if(!x||R<l||r<L) {y=0; return;}
if(L<=l&&r<=R) {y=x; x=0; return;}
int mid=(l+r)>>1;
pushdown(x,dep);
y=++cnt;
split(ch[x][0],ch[y][0],dep-1,l,mid,L,R);
split(ch[x][1],ch[y][1],dep-1,mid+1,r,L,R);
pushup(x); pushup(y);
}
int merge(int x,int y,int dep)
{
if(dep<0)
{
if(x) return x;
return y;
}
if(!x||!y) return x+y;
pushdown(x,dep);pushdown(y,dep);
ch[x][0]=merge(ch[x][0],ch[y][0],dep-1);
ch[x][1]=merge(ch[x][1],ch[y][1],dep-1);
pushup(x);
return x;
}
void update(int u,int dep,int t)
{
if(!u) return;
int tmp=t&ta[u];
if((tmp&to[u])==0)
{
pushxor(u,dep,tmp);
return;
}
pushdown(u,dep);
if((t>>dep)&1)
{
pushxor(ch[u][0],dep-1,(1<<dep));
ch[u][1]=merge(ch[u][0],ch[u][1],dep-1);
ch[u][0]=0;
}
update(ch[u][0],dep-1,t);
update(ch[u][1],dep-1,t);
pushup(u);
}
int n,q;
int main()
{
n=read(); q=read();
fo(i,1,n) ins(rt,19,read());
for(int opt,l,r,x,k;q--;)
{
opt=read(); l=read(); r=read();
k=0; split(rt,k,19,0,W,l,r);
if(opt==4)
{
printf("%d\n",s[k]);
}
else
{
x=read();
if(opt==3) pushxor(k,19,x);
else if(opt==2) update(k,19,x);
else
{
x^=W;
pushxor(k,19,W);
update(k,19,x);
pushxor(k,19,W);
}
}
rt=merge(rt,k,19);
}
return 0;
}