by lby in smwcDay10
1. AGC052B
link
题意
给定一棵
n
n
n 个结点的树(
n
n
n 是奇数),对于任意一条边
(
u
i
,
v
i
,
w
i
,
1
,
w
i
,
2
)
(u_i,v_i,w_{i,1},w_{i,2})
(ui,vi,wi,1,wi,2) ,可以将除这条边之外的和
u
i
u_i
ui 或
v
i
v_i
vi 相连的所有边异或上
w
i
,
1
w_{i,1}
wi,1,问最后是否可以让每条边的边权变为
w
i
,
2
w_{i,2}
wi,2 。
n
≤
1
0
5
,
w
≤
2
30
n \le 10^5 , w \le 2^{30}
n≤105,w≤230
思路
考虑将操作简化,
将边权转化为点权
\color{blue}{将边权转化为点权}
将边权转化为点权 。如何构造,令
w
=
a
u
⊕
a
v
w=a_u \oplus a_v
w=au⊕av ,即让根的点权
a
r
o
o
t
a_{root}
aroot 取
[
0
,
2
30
−
1
]
[0,2^{30}-1]
[0,230−1] 之中任意一个数,往下推即可得到整棵树的
a
i
a_i
ai 。那么操作就是交换
a
u
,
a
v
a_u,a_v
au,av 即可,可以画图理解。所以问题就转化成:对两棵树
A
,
B
A,B
A,B(边权分别为
w
i
,
1
,
w
i
,
2
w_{i,1},w_{i,2}
wi,1,wi,2)分别求出两棵树的点权集合
{
a
i
}
,
{
b
i
}
\{a_i\},\{b_i\}
{ai},{bi},比较这两个集合是否相等。
但由于构造不是唯一的,就是说根的点权不唯一,假设根的点权需异或上
x
x
x 才可行,此时这整棵树的点权都会异或上
x
x
x ,会存在一种匹配方案满足
a
1
⊕
x
=
b
i
1
,
a
2
⊕
x
=
b
i
2
,
⋯
,
a
n
⊕
x
=
b
i
n
a_1 \oplus x=b_{i_1},a_2 \oplus x=b_{i_2},\cdots,a_n \oplus x=b_{i_n}
a1⊕x=bi1,a2⊕x=bi2,⋯,an⊕x=bin ,变换一下式子,就有
a
1
⊕
b
i
1
=
x
,
a
2
⊕
b
i
2
=
x
,
⋯
,
a
n
⊕
b
i
n
=
x
a_1 \oplus b_{i_1}=x,a_2 \oplus b_{i_2}=x,\cdots,a_n \oplus b_{i_n}=x
a1⊕bi1=x,a2⊕bi2=x,⋯,an⊕bin=x,将所有式子异或起来
⊕
i
=
1
n
(
a
i
⊕
b
i
)
=
x
⊕
x
⊕
⋯
⊕
x
⏟
n
个
x
\oplus_{i=1}^n (a_i \oplus b_i) =\underbrace{ x \oplus x \oplus \dots \oplus x}_{n个x}
⊕i=1n(ai⊕bi)=n个x
x⊕x⊕⋯⊕x,又因为
n
n
n 为奇数,所以异或
n
n
n 次
x
x
x 会等于
x
x
x ,所以上面的式子又等于
⊕
i
=
1
n
(
a
i
⊕
b
i
)
=
x
\oplus_{i=1}^n (a_i \oplus b_i) = x
⊕i=1n(ai⊕bi)=x 确定了根的点权为
⊕
i
=
1
n
(
a
i
⊕
b
i
)
\oplus_{i=1}^n (a_i \oplus b_i)
⊕i=1n(ai⊕bi) 之后就可以解决问题了。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int idx,head[maxn];
struct EDGE{ int v,next,w,w2; }e[maxn*2];
void Insert(int u,int v,int w,int w2){
e[++idx]={v,head[u],w,w2};
head[u]=idx;
}
int f[maxn],f2[maxn];
void Dfs(int x,int fa,bool kt){
for(int i=head[x];i;i=e[i].next){
int v=e[i].v;
if(v!=fa){
if(!kt) f[v]=f[x]^e[i].w;//在 Dfs 之前!
else f2[v]=f2[x]^e[i].w2;
Dfs(v,x,kt);
}
}
}
int n;
bool Check(int f[],int f2[]){
for(int i=1;i<=n;i++)
if(f[i]!=f2[i]) return false;
return true;
}
int main(){
cin>>n;
for(int i=1;i<n;i++){
int x,y,z,z2; cin>>x>>y>>z>>z2;
Insert(x,y,z,z2),Insert(y,x,z,z2);
}
f[1]=0; Dfs(1,0,0);
f2[1]=0; Dfs(1,0,1);//f[1]=f2[1] !!!
sort(f+1,f+n+1),sort(f2+1,f2+n+1);
if(Check(f,f2)){
cout<<"YES";
return 0;
}
int sum=0;
for(int i=1;i<=n;i++)
sum^=(f[i]^f2[i]);
f2[1]=sum; Dfs(1,0,1);
sort(f2+1,f2+n+1);
if(Check(f,f2)){
cout<<"YES";
return 0;
}
cout<<"NO";
return 0;
}
2. AGC052C
link
题意
给定质数
P
P
P,计数满足以下条件的长度为
N
N
N 的序列个数:
- 每一个元素在 [ 1 , P − 1 ] [1,P−1] [1,P−1] 之间;
- 可以重排这个序列,使得它的任意一个前缀和都不能够被 P P P 整除。
答案对 998244353 998244353 998244353 取模。 1 ≤ N ≤ 50000 , 1 ≤ P ≤ 1 0 8 1 \le N \le 50000,1 \le P \le 10^8 1≤N≤50000,1≤P≤108
思路
发现直接计数不好做,考虑容斥,什么情况重排不了:考虑一个一个放数,如果当前剩下的数有不同的,那必然不会出现模
P
P
P 余
0
0
0 的情况。也就是说,如果没有数的个数超过
⌊
n
2
⌋
\lfloor \frac{n}{2} \rfloor
⌊2n⌋,必然合法;否则,令这个数为
k
k
k ,由于
P
P
P 是质数,所以我们可以同时乘
k
−
1
k^{-1}
k−1 (即
k
k
k 的逆元),显然不影响结果,此时可以认为这
N
N
N 个数包括 个数
>
⌊
n
2
⌋
\gt \lfloor \frac{n}{2} \rfloor
>⌊2n⌋ 的
1
1
1 和其他一堆非
1
1
1 的数。
这样的话我们可以贪心放置,先放
P
−
1
P−1
P−1 个
1
1
1,放一个非
1
1
1 的数,再接着放
1
…
1 \dots
1… 记序列
b
b
b 为原序列
a
a
a 中非
1
1
1 的数,则只有当
1
1
1 的个数
≤
P
−
1
+
∑
(
P
−
b
i
)
\le P−1+\sum (P−b_i)
≤P−1+∑(P−bi) 才可行。
考虑计数。首先计算 和不为
P
P
P 的排列有多少个。记
a
i
a_i
ai 表示和为
P
P
P 的倍数的长度为
i
i
i 的排列数量,
b
i
b_i
bi 表示
不为
P
的倍数的长度为
i
的数组数量
P
−
1
\frac{不为 P 的倍数的长度为 i 的数组数量}{P−1}
P−1不为P的倍数的长度为i的数组数量,转移为:
a
i
=
b
i
−
1
×
(
P
−
1
)
a_i=b_{i-1} \times (P-1)
ai=bi−1×(P−1)
b
i
=
a
i
−
1
×
(
P
−
1
)
+
b
i
−
1
×
(
P
−
1
)
×
(
P
−
2
)
P
−
1
=
a
i
−
1
+
b
i
−
1
×
(
P
−
2
)
\begin {aligned} b_i &= \frac{a_{i-1} \times (P-1) +b_{i-1} \times (P-1) \times (P-2)}{P-1} \\ &= a_{i-1}+b_{i-1} \times (P-2) \end {aligned}
bi=P−1ai−1×(P−1)+bi−1×(P−1)×(P−2)=ai−1+bi−1×(P−2)
解释一下:
- a i a_i ai 中因为 b i − 1 b_{i-1} bi−1 的定义 / ( P − 1 ) /(P-1) /(P−1) ,所以要乘回来,而第 i i i 个数因为要将和补为 P P P 的倍数,所以只有一种情况, × 1 \times 1 ×1 可以省略。
-
b
i
b_i
bi 中:
- a i − 1 × ( P − 1 ) a_{i-1} \times (P-1) ai−1×(P−1) 意思是前面 i − 1 i-1 i−1 个数的和模 P P P 为 0 0 0,那么第 i i i 个数取 [ 1 , P − 1 ] [1,P-1] [1,P−1] 都不会再让和为 P P P 的倍数;
-
b
i
−
1
×
(
P
−
1
)
×
(
P
−
2
)
b_{i-1} \times (P-1) \times (P-2)
bi−1×(P−1)×(P−2) 中:
- × ( P − 1 ) \times (P-1) ×(P−1) 是抵消定义中的 / ( P + 1 ) /(P+1) /(P+1) ;
- × ( P − 2 ) \times (P-2) ×(P−2) 是因为第 i i i 个数可以选择 [ 1 , P − 1 ] [1,P-1] [1,P−1] 中除了会将和补至 P P P 的倍数的那个数外其他的数,共 ( P − 2 ) (P-2) (P−2) 种。
最后得到
b
N
×
(
P
−
1
)
b_N \times(P-1)
bN×(P−1) 表示长度为
N
N
N 的排列中数的和非
P
P
P 倍数的个数。考虑减去不符合条件的方案数(即 “
1
1
1” 的个数
≤
P
−
1
+
∑
(
P
−
b
i
)
\le P−1+\sum (P−b_i)
≤P−1+∑(P−bi))。
记
f
i
,
j
f_{i,j}
fi,j 表示有
i
i
i 个非
1
1
1 的数(
b
1
b_{1}
b1~
b
i
b_{i}
bi),
∑
c
=
1
i
(
P
−
b
c
)
=
j
\sum\limits_{c=1}^{i} (P−b_c)=j
c=1∑i(P−bc)=j 的方案数。考虑转移,有
b
i
b_i
bi 的取值范围为
[
2
,
P
−
1
]
[2,P-1]
[2,P−1]。下面的
j
−
1
j-1
j−1 由
j
−
(
P
−
(
P
−
1
)
)
j-(P-(P-1))
j−(P−(P−1)) 化简得到。
f
i
,
j
=
∑
k
=
j
−
(
P
−
2
)
j
−
1
f
i
−
1
,
k
f_{i,j}=\sum\limits_{k=j-(P-2)}^{j-1} f_{i-1,k}
fi,j=k=j−(P−2)∑j−1fi−1,k
所以不符合条件的方案数为:
∑
i
=
0
n
∑
j
=
0
n
[
N
−
i
−
j
≢
0
(
m
o
d
P
)
]
[
N
−
i
>
j
+
P
−
1
]
f
i
,
j
×
(
N
i
)
×
(
P
−
1
)
\sum\limits_{i=0}^{n} \sum\limits_{j=0}^{n} [N-i-j \not\equiv 0 \; (mod \, P)]\; [N-i \gt j+P-1] \;f_{i,j}\times \binom{N}{i} \times (P-1)
i=0∑nj=0∑n[N−i−j≡0(modP)][N−i>j+P−1]fi,j×(iN)×(P−1)
解释上面式子:
- 第一个条件表示和不为 p p p 的倍数,序列之和 = 1 的个数 × 1 + 非 1 的数之和 = ( N − i ) × 1 + ∑ b i = 1 \; 的个数 \times 1+非 \;1 \; 的数之和 = (N-i) \times 1 + \sum b_i =1的个数×1+非1的数之和=(N−i)×1+∑bi,又因为 j = ∑ c = 1 i ( P − b c ) = i × P − ∑ c = 1 i b c j=\sum\limits_{c=1}^{i} (P−b_c) = i \times P -\sum\limits_{c=1}^{i} b_c j=c=1∑i(P−bc)=i×P−c=1∑ibc, i × P i \times P i×P 模 P P P 为 0 0 0 所以可以忽略,所以序列之和模 P = ( N − i − j ) m o d P P = (N - i -j) \mod P P=(N−i−j)modP;
- 第二个条件表示不符合条件,可由 1 的个数 > P − 1 + ∑ ( P − b i ) 1的个数 \gt P−1+\sum (P−b_i) 1的个数>P−1+∑(P−bi) 推理得到;
- × ( N i ) \times \binom{N}{i} ×(iN) 表示选出不为 1 1 1 的位置;
- × ( P − 1 ) \times (P-1) ×(P−1) 是因为众数不一定为 1 1 1,有可能是 [ 1 , P − 1 ] [1,P-1] [1,P−1] 中任意一个,所以还要乘 ( P − 1 ) (P-1) (P−1),不会算重是因为此时的众数一定是绝对众数了。
D P DP DP 中还可以前缀和优化。总时间复杂度为 O ( N 2 ) O(N^2) O(N2) 。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5005,mod=998244353;
ll Pow_(ll x,ll y){
ll s=1;
while(y){
if(y&1) (s*=x)%=mod;
(x*=x)%=mod;
y>>=1;
}
return s;
}
ll fac[maxn],inv_fac[maxn];
ll C(int m,int n){ return fac[m]*inv_fac[n]%mod*inv_fac[m-n]%mod; }
ll fsa[maxn],fsb[maxn],f[maxn][maxn];
int main(){
int n,p; cin>>n>>p;
fac[0]=inv_fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=fac[i-1]*i%mod;
inv_fac[i]=Pow_(fac[i],mod-2);
}
fsa[0]=1;
for(int i=1;i<=n;i++){
fsa[i]=fsb[i-1]*(p-1)%mod;
fsb[i]=(fsa[i-1]+fsb[i-1]*(p-2)%mod)%mod;
}
ll ans=fsb[n]*(p-1)%mod;
f[0][0]=1;
for(int i=1;i<=n;i++){
ll sum=0;
for(int j=1;j<=n;j++){
(sum+=f[i-1][j-1])%=mod;
if(j-(p-2)-1>=0) sum-=f[i-1][j-(p-2)-1],(sum+=mod)%=mod;
f[i][j]=sum;
}
}
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)//j 从 0 开始!!!
if((n-i-j)%p!=0&&n-i>j+p-1) ans-=f[i][j]*C(n,i)%mod*(p-1)%mod,(ans+=mod)%=mod;
cout<<ans;
return 0;
}
3. CF547D
link
题意
给定
n
n
n 个整点,你要给每个点染成红色或蓝色,要求同一水平线或垂直线上两种颜色的数量最多相差
1
1
1,求一种方案。
n
≤
1
0
5
n \le 10^5
n≤105
思路
考虑如何建图,对于每一行的点都任意两两配对(连边),每个点都只能选择一次,让匹配出来的一组点一个染红一个染蓝,每一行最多剩下
1
1
1 个点,那就随便染。对于每一列也一样。每个点最多连
2
2
2 条边。所成的环也只会是偶环,二分图染色即可,可以验证这样构造时满足题意的。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int idx,head[maxn];
struct EDGE{ int v,next; }e[maxn*2];
void Insert(int u,int v){
e[++idx]={v,head[u]};
head[u]=idx;
}
int pnt[maxn];
bool vis[maxn];
void Dfs(int x,int fa){
vis[x]=1;
for(int i=head[x];i;i=e[i].next){
int v=e[i].v;
if(v!=fa&&!vis[v]){
pnt[v]=pnt[x]^1;
Dfs(v,x);
}
}
}
struct NODE{ int x,y; }a[maxn];
vector<int>vt[maxn*2];
int main(){
int n; cin>>n;
int mar=0,mac=0;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
vt[a[i].x].push_back(i);
mar=max(mar,a[i].x);
}
for(int i=1;i<=n;i++){
vt[a[i].y+mar].push_back(i);
mac=max(mac,a[i].y);
}
for(int i=1;i<=mar;i++)
for(int j=0;j<vt[i].size();j+=2)
if(j+1<vt[i].size()) Insert(vt[i][j],vt[i][j+1]),Insert(vt[i][j+1],vt[i][j]);
for(int i=mar+1;i<=mar+mac;i++)
for(int j=0;j<vt[i].size();j+=2)
if(j+1<vt[i].size()) Insert(vt[i][j],vt[i][j+1]),Insert(vt[i][j+1],vt[i][j]);
for(int i=1;i<=n;i++)
if(!vis[i]) Dfs(i,0);
for(int i=1;i<=n;i++)
cout<<(pnt[i]?"r":"b");
return 0;
}
4. CF1515E
link
题意
一排关机的电脑(共
n
n
n 个),你每次可以选一个关机的开机,如果一台电脑左右相邻的两台都开机了,那么它自动开机,求操作方案数。
n
≤
400
n \le 400
n≤400
思路