[ARC089D] ColoringBalls
天,感觉全是细节,事实上也如此:
借大佬的细节才过了此题
#include<bits/stdc++.h>
#define maxn 75
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 1000000007
#define vi vector<int>
#define pb push_back
using namespace std;
int n,K,fac[maxn << 2],invf[maxn << 2],inv[maxn << 2];
int C(int a,int b){ if(a<0 || b<0 || a-b<0) return 0;return fac[a] * 1ll * invf[b] % mod * invf[a-b]%mod; }
char s[maxn];
vi p;int ans;
bool check(){
static bool vis[maxn];
static int loc[maxn];
memset(vis,0,sizeof vis);
int j=1;
for(int i=p.size()-1;i>=0 && p[i];i--){
for(;j <= K && s[j] != 'r';j++);
if(j > K) return 0;
vis[j] = 1 , loc[i] = j , j++;
}
j=1;
for(int i=p.size()-1;i>=0 && p[i];i--){
j = max(j , loc[i]);
for(;j <= K && s[j] != 'b';j++);
if(j > K) return 0;
vis[j] = 1 , loc[i] = j , j++;
}
j=1;
for(int i=0;i<p.size() && p[i]==0;i++){
for(;j <= K && (s[j] != 'r' || vis[j]);j++);
if(j > K) return 0;
vis[j] = 1 , j++;
}
j=1;
for(int i=p.size()-1;i>=0 && p[i] > 1;i--){
j = max(j , loc[i]);
rep(k,2,p[i]){
for(;j <= K && vis[j];j++);
if(j > K) return 0;
vis[j] = 1 , j++;
}
}
return 1;
}
void dfs(int sz,int mx,int lim){
if(sz > n) return;
if(check()){
int sm = fac[p.size()];
for(int i=0,j;i<p.size();i=j){
for(j=i;j<p.size() && p[j] == p[i];j++);
sm = 1ll * sm * invf[j-i] % mod;
}
ans = (ans + 1ll * C(n-sz+mx-1,mx-1) * sm) % mod;
//printf("%d %d %d %d\n",sm,ans,sz,mx);
}
else return;
rep(i,lim,(n-sz+1)/2){
p.pb(i);
dfs(sz+max(2*i-1,1)+(sz!=0),mx+2*i+2,i);
p.pop_back();
}
}
int main(){//freopen("1.in","r",stdin);//freopen("2.out","w",stdout);
scanf("%d%d",&n,&K);
scanf("%s",s+1);
fac[0] = inv[0] = inv[1] = fac[1] = invf[0] = invf[1] = 1;
rep(i,2,(maxn << 2) - 1) fac[i] = 1ll * fac[i-1] * i % mod , inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,
invf[i] = 1ll * invf[i-1] * inv[i] % mod;
dfs(0,1,0);
printf("%d\n",(ans+mod)%mod);
}
[ARC096C] Everything on It
容斥,枚举有
k
k
k种元素用了
≤
1
\leq 1
≤1次,其中有
j
j
j种元素用了
1
1
1次。
则答案为:
∑
k
=
0
n
(
−
1
)
k
(
n
k
)
2
2
n
−
k
∑
j
=
0
k
(
k
j
)
∑
p
=
0
j
{
j
p
}
2
(
n
−
k
)
p
\sum_{k=0}^n(-1)^k\binom nk2^{2^{n-k}}\sum_{j=0}^k \binom kj\sum_{p=0}^j \begin{Bmatrix}j\\p\end{Bmatrix}2^{(n-k)p}
k=0∑n(−1)k(kn)22n−kj=0∑k(jk)p=0∑j{jp}2(n−k)p
注意
2
2
n
−
k
2^{2^{n-k}}
22n−k和
(
2
2
)
n
−
k
(2^2)^{n-k}
(22)n−k是完全不同的。
注意到有恒等式
∑
j
=
p
k
(
k
j
)
{
j
p
}
=
{
k
+
1
p
+
1
}
\sum_{j=p}^k\binom kj \begin{Bmatrix} j\\p \end{Bmatrix}=\begin{Bmatrix} k+1\\p+1 \end{Bmatrix}
j=p∑k(jk){jp}={k+1p+1}
证明:考虑组合意义,相当于是拿
k
−
j
k-j
k−j个数不选,剩下的
j
j
j个数分到
p
p
p组内,如果我们加一个组来装着
k
−
j
k-j
k−j个数,那么这个组可能为空,同时发现这个组和别的组不一样,但是第二类斯特林数组之间是没有标号区别的,所以我们就往空组加入一个
0
0
0(强行规定
0
0
0所在的组为不选组),则非空和有区别这两个限制都被满足了,方案数就是
{
k
+
1
p
+
1
}
\begin{Bmatrix} k+1\\p+1 \end{Bmatrix}
{k+1p+1}。
但是其实这样做很傻,直接类似第二类斯特林数
d
p
dp
dp,
g
i
,
j
=
g
i
−
1
,
j
−
1
+
(
j
+
1
)
g
i
−
1
,
j
g_{i,j} = g_{i-1,j-1} + (j+1)g_{i-1,j}
gi,j=gi−1,j−1+(j+1)gi−1,j
后面那个
j
+
1
j+1
j+1中的
+
1
+1
+1就代表了不选这一选项,直接
d
p
dp
dp也没有什么问题。
a n s = ∑ k = 0 n ( − 1 ) k ( n k ) 2 2 n − k ∑ p = 0 k { k + 1 p + 1 } 2 ( n − k ) p ans = \sum_{k=0}^n(-1)^k\binom nk2^{2^{n-k}}\sum_{p=0}^k \begin{Bmatrix}k+1\\p+1\end{Bmatrix}2^{(n-k)p} ans=k=0∑n(−1)k(kn)22n−kp=0∑k{k+1p+1}2(n−k)p
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 3005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,P,S[maxn][maxn],C[maxn][maxn],pw[maxn];
int upd(int x){ return x += x >> 31 & P; }
int main(){
scanf("%d%d",&n,&P);
rep(i,S[0][0]=C[0][0]=1,n) rep(j,C[i][0]=1,i) C[i][j] = upd(C[i-1][j-1] + C[i-1][j] - P) % P;
rep(i,1,n) rep(j,S[i][0]=1,i) S[i][j] = (S[i-1][j] * (j+1ll) + S[i-1][j-1]) % P;
int ans = 0 , ppw = 2;
rep(i,0,n) pw[i] = 1;
per(k,n,0){
int sm = 0;
rep(i,0,k) sm = (sm + 1ll * S[k][i] * pw[i]) % P;
ans = (ans + (k&1?-1ll:1ll)*C[n][k]*ppw%P*sm)%P;
int p2 = 1;
rep(i,0,n) pw[i] = 1ll * p2 * pw[i] % P , p2 = 2ll * p2 % P;
ppw = ppw * 1ll * ppw % P;
}
printf("%d\n",(ans+P)%P);
}
CF1295F Good Contest
题意: a i a_i ai为在 [ L i , R i ] [L_i,R_i] [Li,Ri]之间均匀随机的离散变量,求 a i a_i ai不增的概率。
先离散化,将
L
,
R
L,R
L,R离散化后排序得到一个数组
a
a
a
设
f
i
,
j
f_{i,j}
fi,j表示前
i
i
i个变量最后一个变量
≥
a
j
\geq a_j
≥aj的概率。
那么
f
i
,
j
=
∑
f
k
,
j
+
1
×
(
a
j
+
1
−
a
j
+
i
−
k
−
1
i
−
k
)
f_{i,j} = \sum f_{k,j+1} \times \binom {a_{j+1}-a_j+i-k-1}{i-k}
fi,j=∑fk,j+1×(i−kaj+1−aj+i−k−1)
后面那个组合数就是在
[
a
j
,
a
j
+
1
)
[a_j,a_{j+1})
[aj,aj+1)中选择
j
−
k
j-k
j−k个可以相同的数但是需要无序的方案。(因为最后分配给
k
+
1...
i
k+1...i
k+1...i的变量是有序的。)
注意这个转移必须要
k
+
1...
i
k+1...i
k+1...i都包含
[
a
j
,
a
j
+
1
)
[a_j,a_{j+1})
[aj,aj+1)这个区间才行。
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 105
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 998244353
using namespace std;
int n,l[maxn],r[maxn],sb[maxn<<1];
int C[maxn][maxn],inv[maxn],f[maxn][maxn],in[maxn][maxn];
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }
int main(){
inv[0] = inv[1] = 1;
int sm = 1;
for(int i=2;i<maxn;i++) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&l[i],&r[i]),sb[++sb[0]]=l[i],sb[++sb[0]]=(++r[i]) , sm = 1ll * sm * Pow(r[i] - l[i] , mod-2) % mod;
sort(sb+1,sb+1+sb[0]);
sb[0] = unique(sb+1,sb+1+sb[0])-sb-1;
for(int i=1;i<sb[0];i++){
int L = sb[i+1] - sb[i] , f = 1;
rep(j,1,n){
f = 1ll * f * (L+j-1) % mod * inv[j] % mod;
C[i][j] = f;
}
}
rep(i,1,sb[0]) f[0][i] = sm;
for(int i=1;i<=n;i++){
l[i] = lower_bound(sb+1,sb+1+sb[0],l[i])-sb , r[i] = lower_bound(sb+1,sb+1+sb[0],r[i])-sb;
rep(j,l[i],r[i]-1){
in[i][j] = 1;
for(int k=i-1;in[k+1][j] && k>=0;k--)
f[i][j] = (f[i][j] + 1ll * f[k][j+1] * C[j][i-k]) % mod;
}
per(j,sb[0]-1,1) f[i][j] = (f[i][j] + f[i][j+1]) % mod;
}
printf("%d\n",(f[n][1]+mod)%mod);
}
Valentines Day Contest 2020 C. Isolation
给出 ( x , y ) (x,y) (x,y),求走 n n n步(每步可以让横坐标或纵坐标 ± 1 \pm 1 ±1)的方案数使得途中不经过距离原点曼哈顿距离为 D ≤ 4 D \leq 4 D≤4的点。
容斥,设 f i , j , k f_{i,j,k} fi,j,k表示走了 i i i步之后到达了 j j j号禁止点(禁止点为距离原点曼哈顿距离为 D D D的点集),至少一共走过了 k k k个禁止点。
所以答案就是
4
n
+
∑
(
−
1
)
k
f
i
,
j
,
k
4
n
−
i
4^n + \sum (-1)^k f_{i,j,k}4^{n-i}
4n+∑(−1)kfi,j,k4n−i
注意到我们直接计算容斥系数可以不需要
k
k
k这一维。
所以
f
i
,
j
f_{i,j}
fi,j在我们可以
O
(
1
)
O(1)
O(1)直接计算两个点之间走
p
p
p步到达的方案数后,
复杂度就是
O
(
n
2
D
2
)
O(n^2D^2)
O(n2D2)的。
设一个点在
(
a
,
b
)
(a,b)
(a,b)另一个点在
(
c
,
d
)
(c,d)
(c,d),一共走
p
p
p步要到达。
发现因为有相反的方向(减少的方向)所以计数很困难,考虑如何用上一共走
p
p
p步的条件。
如果我们把减少横坐标看做增加纵坐标,减少纵坐标看做增加横坐标,
那么横坐标的增加量比纵坐标的增加量大
c
−
a
+
b
−
d
c-a+b-d
c−a+b−d,总步数又是
p
p
p,所以我们可以得出增加纵坐标的次数是
p
−
(
c
−
a
+
b
−
d
)
2
\frac {p - (c-a+b-d)}2
2p−(c−a+b−d),方案数为
(
p
p
−
(
c
−
a
+
b
−
d
)
2
)
\binom {p}{\frac {p - (c-a+b-d)}2}
(2p−(c−a+b−d)p)
但是这样无法区分增加纵坐标和减少横坐标这两种操作(等),
于是我们再反着定义:
把减少横坐标看做增加横坐标,减少纵坐标看做增加横坐标,增加横坐标看做增加纵坐标,增加纵坐标不变。
那么纵坐标的增加量比横坐标的增加量大
c
−
a
+
d
−
b
c-a+d-b
c−a+d−b。
方案数为
(
p
p
−
(
c
−
a
+
d
−
b
)
2
)
\binom {p}{\frac {p - (c-a+d-b)}2}
(2p−(c−a+d−b)p)
然后发现这样定义,两个组合数的乘积就是我们需要的答案。
如果我们记横坐标之差为
x
x
x,纵坐标之差为
y
y
y的话答案就是:
(
p
p
−
x
−
y
2
)
(
p
p
−
x
+
y
2
)
\binom{p}{\frac {p-x-y}2} \binom{p}{\frac {p-x+y}2}
(2p−x−yp)(2p−x+yp)
这个方法感觉有点奇怪,特别是拓展到三维的时候他是四个组合数乘起来。
但是他可以
O
(
1
)
O(1)
O(1)。
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 3005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 1000000007
using namespace std;
int X,Y,n,D,c[maxn][maxn];
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1)r=1ll*r*b%mod;return r; }
int C(int n,int m){ if(m < 0 || n < 0 || n - m < 0) return 0; return c[n][m]; }
int B(int a,int b,int p){
if(p-a-b&1) return 0;
return C(p,(p-a-b)/2) * 1ll * C(p,(p-a+b)/2) % mod;
}
int x[maxn],y[maxn],cnt;
int f[maxn][maxn];
int main(){
scanf("%d%d%d%d",&X,&Y,&n,&D);
rep(i,c[0][0]=1,maxn-1) rep(j,c[i][0]=1,i) c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
rep(i,-D,D) rep(j,-D,D) if(abs(i) + abs(j) == D)
x[++cnt] = i , y[cnt] = j;
rep(i,1,n) rep(j,1,cnt){
f[i][j] = -B(abs(x[j]-X),abs(y[j]-Y),i);
rep(k,1,i-1) rep(p,1,cnt)
f[i][j] = (f[i][j] - 1ll * f[k][p] * B(x[j]-x[p],y[j]-y[p],i-k)) % mod;
}
int ans = Pow(4 , n);
rep(i,1,n) rep(j,1,cnt) ans = (ans + f[i][j] * 1ll * Pow(4,n-i)) % mod;
printf("%d\n",(ans+mod)%mod);
}
AGC034F RNG and XOR
有 A i A_i Ai概率选 i i i来异或你手上的数,问第一次得到 1.... 2 n − 1 1....2^n-1 1....2n−1的期望时间。
怎么感觉ZJOI2019开关就是把这道题的FWT拆了拆式子快速实现。
发现多次到达同一个数很难解决,但是发现如果倒过来我们求从
1...
2
n
−
1
1...2^n-1
1...2n−1第一次到
0
0
0的时间并且让
0
0
0不能转移出来就可以解决多次到达同一个数的问题,因为这样同一个数都变成了
0
0
0,便于统一处理。
所以设
f
i
f_i
fi表示从
i
i
i第一次到
0
0
0的时间。
则有
f
i
=
∑
j
f
j
×
A
j
∧
i
+
1
f_i = \sum_{j} f_j \times A_{j\wedge i} + 1
fi=∑jfj×Aj∧i+1,注意这个式子是对
i
i
i进行转移,所以
i
>
0
i \gt 0
i>0。
写成生成函数就是
(
f
0
,
f
1
.
.
.
f
2
n
−
1
)
⊕
(
A
0
,
A
1
.
.
.
A
2
n
−
1
)
=
(
f
0
+
2
n
−
1
,
f
1
−
1
,
f
2
−
1....
f
2
n
−
1
−
1
)
(f_0,f_1...f_{2^n-1}) \oplus(A_0,A_1...A_{2^n-1}) = (f_0+2^n-1,f_1-1,f_2-1....f_{2^n-1}-1)
(f0,f1...f2n−1)⊕(A0,A1...A2n−1)=(f0+2n−1,f1−1,f2−1....f2n−1−1)
注意
f
0
+
2
n
−
1
f_0+2^n-1
f0+2n−1不是由上面的方程得到的,而是因为
A
A
A的和为
1
1
1,所以卷积之后所有项的和不变,所以解出来的。
那么我们将
A
0
A_0
A0变成
A
0
−
1
A_0-1
A0−1则可以发现。
(
f
0
,
f
1
.
.
.
f
2
n
−
1
)
⊕
(
A
0
−
1
,
A
1
.
.
.
A
2
n
−
1
)
=
(
2
n
−
1
,
−
1
,
−
1....
−
1
)
(f_0,f_1...f_{2^n-1}) \oplus(A_0-1,A_1...A_{2^n-1}) = (2^n-1,-1,-1....-1)
(f0,f1...f2n−1)⊕(A0−1,A1...A2n−1)=(2n−1,−1,−1....−1)
直接写
F
W
T
FWT
FWT除法即可,注意到
∑
A
i
−
1
=
0
\sum A_i - 1 = 0
∑Ai−1=0也就是说我们
F
W
T
FWT
FWT后被除的式子中
2
n
−
1
2^n-1
2n−1这项为
0
0
0(因为
A
i
≥
1
A_i \geq 1
Ai≥1别的都不为
0
0
0),注意到这东西表达的方程是
f
0
+
f
1
+
.
.
.
f
2
n
−
1
×
0
=
0
f_0+f_1+...f_{2^n-1} \times 0 = 0
f0+f1+...f2n−1×0=0,我们考虑就这样让
f
0
+
f
1
+
.
.
.
f
2
n
−
1
=
0
f_0+f_1+...f_{2^n-1}=0
f0+f1+...f2n−1=0,发现这个方程可能会无解,因为我们加多了条件,但是没关系,我们解方程是用
I
F
W
T
IFWT
IFWT解,不会考虑无解。
I
F
W
T
IFWT
IFWT之后如果得到了
f
0
′
,
f
1
′
,
f
2
′
.
.
.
f
2
n
−
1
′
f_0',f_1',f_2'...f_{2^n-1}'
f0′,f1′,f2′...f2n−1′,考虑怎么得到
f
0
,
f
1
.
.
.
f
2
n
−
1
f_0,f_1...f_{2^n-1}
f0,f1...f2n−1,假设真实的
f
0
+
f
1
+
.
.
.
f
2
n
−
1
=
X
f_0+f_1+...f_{2^n-1} = X
f0+f1+...f2n−1=X。
那么在
F
W
T
FWT
FWT之后
f
i
′
=
f
i
−
X
2
n
f_i' = f_i - \frac {X}{2^n}
fi′=fi−2nX,因为
f
0
′
=
f
0
−
X
2
n
=
−
X
2
n
f_0' = f_0 - \frac X{2^n} = -\frac X{2^n}
f0′=f0−2nX=−2nX
所以每个位置都减去
f
0
′
f_0'
f0′即可。
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 1<<18|5
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,N;
int a[maxn],b[maxn];
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }
void FWT(int *a){
rep(i,0,n-1) rep(j,0,N-1) if(!(j>>i&1)){
int v = j ^ (1 << i) , x = (a[j] + a[v]) % mod , y = (a[j] - a[v]) % mod;
a[j] = x , a[v] = y;
}
}
int main(){
scanf("%d",&n);N = 1 << n;int S=0;
rep(i,0,N-1) scanf("%d",&a[i]),S+=a[i];
S = Pow(S , mod-2);
rep(i,0,N-1) a[i] = 1ll * a[i] * S % mod;
a[0]-- , b[0] = 1 << n;
rep(i,0,N-1) b[i]--;
FWT(a),FWT(b);
rep(i,0,N-1) a[i] = 1ll * Pow(a[i],mod-2) * b[i] % mod;
FWT(a);int ivN = Pow(N , mod-2);
rep(i,0,N-1) a[i] = 1ll * a[i] * ivN % mod;
per(i,N-1,0) a[i] = (a[i] - a[0]) % mod;
rep(i,0,N-1) printf("%d\n",(a[i]+mod)%mod);
}
2019-2020 XX Opencup GP of Tokyo E . Count Modulo 2
给出 A 1 . . . A K A_1...A_K A1...AK,求 a 1 + a 2 . . . + a n = S a_1+a_2...+a_n = S a1+a2...+an=S的方案数 m o d 2 \bmod 2 mod2,其中 a i a_i ai是 A 1 . . . A k A_1...A_k A1...Ak中的一个, K ≤ 200 , A i ≤ 1 e 5 , n ≤ 1 e 18 , S ≤ 1 e 18 K\leq 200 , A_i \leq 1e5, n\leq 1e18 , S \leq 1e18 K≤200,Ai≤1e5,n≤1e18,S≤1e18
活生生猜出来的解法
因为要
m
o
d
2
\bmod 2
mod2,所以假设说一个方案中
A
1
.
.
.
A
k
A_1...A_k
A1...Ak各有
b
1
.
.
b
k
b_1..b_k
b1..bk个,那么和他只是顺序不同的方案数有
n
!
∏
b
i
!
−
1
\frac {n!}{\prod b_i!}-1
∏bi!n!−1种。
由库默尔定理我们可以知道一个二进制位
k
k
k只能由一个
b
i
b_i
bi所拥有,这样就可以解决
n
≤
1
e
18
n \leq 1e18
n≤1e18的问题,把
n
n
n的每个二进制位拿出来即可。(有个更牛逼的结论,在
(
m
o
d
2
)
\pmod 2
(mod2)意义下
f
(
x
2
)
≡
f
(
x
)
2
f(x^2) \equiv f(x)^2
f(x2)≡f(x)2)
但是
S
≤
1
e
18
S \leq 1e18
S≤1e18。
考虑
A
i
≤
1
e
5
A_i \leq 1e5
Ai≤1e5,所以当我们在考虑第
k
k
k位的时候,剩余的
S
>
2
k
1
e
5
S \gt2^k1e5
S>2k1e5后面都无法让
S
S
S变为
0
0
0。
所以我们从大到小枚举
k
k
k,用
b
i
t
s
e
t
bitset
bitset保存
1
e
5
1e5
1e5位,然后转移,
k
−
−
k--
k−−的时候需要枚举每个
b
i
t
s
e
t
bitset
bitset位来转移,时间复杂度
O
(
log
n
(
K
v
32
+
v
)
)
O(\log n(\frac {Kv}{32}+v))
O(logn(32Kv+v))
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;
LL n,S;
int a[maxn],K;
bitset<maxn>f[2],t;
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%lld%lld%d",&n,&S,&K);
rep(i,1,K) scanf("%d",&a[i]);
int now = 1 , pre = 0;
f[now].reset(),f[pre].reset();
f[now][0] = 1;
per(i,60,0){
swap(now,pre);
t.reset(),f[now].reset();
if(n >> i & 1){
rep(j,1,K) t ^= f[pre] >> a[j];
}
else t = f[pre];
if(i){
rep(j,0,(maxn-4)/2) if(t[j])
f[now].flip(j << 1 | (S >> (i-1) & 1));
}
else
f[now] = t;
}
cout << f[now][0] << endl;
}
}
2019-2020 XX Opencup GP of Tokyo I. Amidakuji
求 K ≤ ⌈ log 2 n ⌉ + 1 K \leq \lceil \log_2n \rceil + 1 K≤⌈log2n⌉+1个 1... n 1...n 1...n的排列的映射 p i ( x ) p_i(x) pi(x),对于所有 x , y x,y x,y都存在 q K ( q K − 1 ( . . . q 1 ( x ) ) ) = y q_K(q_{K-1}(...q_1(x))) = y qK(qK−1(...q1(x)))=y,其中 q i ( x ) = p i ( x ) q_i(x) = p_i(x) qi(x)=pi(x)或 p i ( x ) − 1 p_i(x)^{-1} pi(x)−1
设
P
=
⌊
log
2
n
⌋
P = \lfloor \log_2n \rfloor
P=⌊log2n⌋
则我们让
p
i
,
j
=
j
+
2
i
m
o
d
n
,
i
∈
[
0
,
P
]
p_{i,j} = j + 2^i \bmod n,i \in [0,P]
pi,j=j+2imodn,i∈[0,P]
这样可以组合出来
n
n
n以内所有奇数,包括负的。
证明可以考虑归纳证明。
如果
n
n
n是奇数,那么正负两边已经能让我们到达所有位置。
如果
n
n
n是四的倍数,可以构造排列
{
2
,
3
,
0
,
1
,
6
,
7
,
4
,
5....
n
−
2
,
n
−
1
,
n
−
4
,
n
−
3
}
\{2,3,0,1,6,7,4,5....n-2,n-1,n-4,n-3\}
{2,3,0,1,6,7,4,5....n−2,n−1,n−4,n−3}来让我们可以使得一个位置移动奇数或偶数,注意这时这个排列是可以让
x
,
y
x,y
x,y的奇偶性不同的,所以
2
P
2^{P}
2P这个排列就不需要了(刚好卡进),我们只需要找距离最近也就是
<
2
P
\lt 2^P
<2P的那边走。
如果
n
n
n是
m
o
d
4
=
2
\bmod 4 = 2
mod4=2,可以构造排列
{
2
,
3
,
0
,
1
,
6
,
7
,
4
,
5....
n
−
4
,
n
−
3
,
n
−
6
,
n
−
5
,
n
−
2
,
n
−
1
}
\{2,3,0,1,6,7,4,5....n-4,n-3,n-6,n-5,n-2,n-1\}
{2,3,0,1,6,7,4,5....n−4,n−3,n−6,n−5,n−2,n−1}和
{
0
,
1
,
2
,
3
,
.
.
.
.
n
−
6
,
n
−
5
,
n
−
2
,
n
−
1
,
n
−
3
,
n
−
4
}
\{0,1,2,3,....n-6,n-5,n-2,n-1,n-3,n-4\}
{0,1,2,3,....n−6,n−5,n−2,n−1,n−3,n−4}即可。
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define maxn 1005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define pb push_back
using namespace std;
int n;
vector<vector<int> >p;
int main(){
scanf("%d",&n);
if(n == 2){
puts("-1");
return 0;
}
int L = 1 , l = 0;
for(;L <= n; L<<=1,l++);
l--;
rep(i,0,l-(n % 2 == 0)){
vector<int>r;
rep(j,0,n-1) r.pb((j+(1<<i)) % n);
p.pb(r);
}
if(n % 4 == 0){
vector<int>r;
rep(j,0,n/4-1)
r.pb(4*j+2),r.pb(4*j+3),r.pb(4*j+1),r.pb(4*j);
p.pb(r);
}
else if(n % 2 == 0){
vector<int>r;
rep(j,0,n/4-1)
r.pb(4*j+2),r.pb(4*j+3),r.pb(4*j+1),r.pb(4*j);
r.pb(n-2),r.pb(n-1);
p.pb(r);
r.clear();
rep(j,0,n-5) r.pb(j);
r.pb(n-4+2),r.pb(n-4+3),r.pb(n-4+1),r.pb(n-4);
p.pb(r);
}
printf("%d\n",p.size());
rep(i,0,p.size()-1)
rep(j,0,n-1)
printf("%d%c",p[i][j]+1," \n"[j==n-1]);
}
CodeForces 1292F Nora’s Toy Boxes
题意:给出 n ≤ 60 n\leq 60 n≤60个不同的 ≤ 60 \leq 60 ≤60的数,当 i , j , k i,j,k i,j,k满足 a i , a j , a k a_i,a_j,a_k ai,aj,ak都未被删去, a i ∣ a j a_i | a_j ai∣aj并且 a i ∣ a k a_i | a_k ai∣ak时可以将 a k a_k ak删去,求能删除最多数的删除序列数。
将
a
i
∣
a
j
a_i | a_j
ai∣aj视作
i
→
j
i \rightarrow j
i→j的连边,则我们对于每个弱连通图分别计算方案。
(注意下文的讨论中图是弱联通的。)
显然这个如果
i
→
j
,
j
→
k
i\rightarrow j,j\rightarrow k
i→j,j→k有边,则
i
→
k
i \rightarrow k
i→k有边。
所以如果在某次删除中需要找到一个
i
i
i,这个
i
i
i一定可以没有入度。
我们把没有入度的点集看做
S
S
S,其他点看做
T
T
T。
则我们需要求最少删到还有多少点,换个方向考虑,假如一开始我们让一些点存在,然后让这些
i
,
j
,
k
i,j,k
i,j,k,
a
i
,
a
j
a_i,a_j
ai,aj存在,
a
k
a_k
ak不存在的拓展出
a
k
a_k
ak存在,如果能拓展出所有点,那么这个拓展方案反过来就和合法的删点方案一一对应。
首先
S
S
S中的点不可能被删,所以
S
S
S中的点一开始都是存在的,删最多的点意味着
T
T
T中一开始存在的点要尽量少,最少为
1
1
1,接下来我们给出构造的方案使得
T
T
T中一开始的点数为
1
1
1。
对于
T
T
T中存在的点,找能够到达他的所有的
S
S
S中的点
x
x
x,那么
x
x
x能到达的点都会变为存在,再重复这个过程即可,容易发现弱连通图中的所有点都会变为存在,也就是
T
T
T中任意一个点开始我们都可以让所有点存在。
考虑如何根据这个过程构造出删点方案,我们定义一种新的标记,这种标记只会打在
S
S
S的点中,对于
T
T
T中一开始的点,我们找能够到达他的所有的
S
S
S中的点
x
x
x,给
x
x
x打上标记,之后我们找下一个被删除的点
y
y
y,这个
y
y
y只需要保证能够到达他的所有的
S
S
S中的点存在一个
p
p
p,
p
p
p是有标记的即可,接下来给能够到达他的所有的
S
S
S中的点
x
x
x打上标记,如此重复即可得到一个删点方案。
可以证明
S
S
S中的点数
≤
60
4
\leq \frac {60}4
≤460,首先可以认为
S
S
S中的点都应该
≤
30
\leq 30
≤30,否则没有出边也没有入边不满足强联通,对于
≤
30
\leq 30
≤30的所有点,
S
S
S中的点构成了一个反链,其大小
≤
1...30
\leq 1...30
≤1...30的最小链覆盖,我们可以给出一个链覆盖为
{
1
,
2
,
4
,
8....
}
,
{
3
,
6
,
12
}
.
.
.
\{1,2,4,8....\},\{3,6,12\}...
{1,2,4,8....},{3,6,12}...等形如一个奇数
a
×
2
k
a\times 2^k
a×2k的
30
2
\frac {30}2
230条链,所以
S
S
S中的点数
≤
15
\leq 15
≤15。
于是我们用一个状压
d
p
dp
dp,
f
s
,
i
f_{s,i}
fs,i表示目前集合
s
s
s的点打上了标记,已经删去了
i
i
i个点,
注意到我们不应该把
T
T
T中的点是否被选过纳入状态,所以我们需要有两种转移。
一种是删去能到达他的只有
s
s
s中的点,那么第一维不变,第二维
+
1
+1
+1,需要预处理出能到达他的只有
s
s
s中的点数
c
c
c,通过
c
−
i
c-i
c−i统计出还没被删的点数来做决策。
第二种是删去能使
s
s
s变大的点并且这个点能被
s
s
s到达,这个点显然不会被删过,所以我们可以有一个
O
(
2
15
n
2
)
O(2^{15} n^2)
O(215n2)的
d
p
dp
dp。
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define maxn 65
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define mod 1000000007
#define vi vector<int>
#define pb push_back
using namespace std;
int n,a[maxn];
int F[maxn],in[maxn],C[maxn][maxn],sta[1<<15],f[1<<15],g[1<<15][maxn];
int Find(int u){ return !F[u] ? u : F[u] = Find(F[u]); }
vi G[maxn];
int main(){
scanf("%d",&n);
rep(i,1,n) scanf("%d",&a[i]);
sort(a+1,a+1+n);
rep(i,1,n) rep(j,i+1,n) if(a[j] % a[i] == 0){
int x = Find(j) , y = Find(i);
in[j]++;
if(x ^ y) F[x] = y;
}
rep(i,C[0][0]=1,n) rep(j,C[i][0]=1,i) C[i][j] = (C[i-1][j-1] + C[i-1][j]) % mod;
rep(i,1,n) G[Find(i)].pb(i);
int ans = 1 , hd = 0;
rep(i,1,n) if(G[i].size() > 1){
vi S;
for(int v:G[i]) if(!in[v]) S.pb(v);
int N = 1 << S.size();
memset(f,0,sizeof f) , memset(g,0,sizeof g);
int cnt = 0;
for(int v:G[i]) if(in[v]){
cnt ++;
rep(j,0,S.size()-1)
if(a[v] % a[S[j]] == 0)
sta[v] |= 1 << j;
f[sta[v]] ++;
}
rep(i,0,S.size()-1) rep(j,0,N-1) if(j >> i & 1)
f[j] += f[j-(1<<i)];
g[0][0] = 1;
rep(j,0,N-1) rep(k,0,cnt) if(g[j][k]){
if(k < f[j]) g[j][k+1] = (g[j][k+1] + 1ll * g[j][k] * (f[j] - k)) % mod;
for(int p:G[i]) if(in[p] && ((sta[p] & j) != sta[p]) && ((sta[p] & j) || j == 0))
g[j | sta[p]][k+1] = (g[j|sta[p]][k+1] + g[j][k]) % mod;
}
ans = 1ll * ans * g[N-1][cnt] % mod * C[hd + cnt - 1][cnt - 1] % mod;
hd += cnt - 1;
}
printf("%d\n",ans);
}