[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
容斥,枚舉有種元素用了次,其中有種元素用了次。
則答案爲:
注意和是完全不同的。
注意到有恆等式
證明:考慮組合意義,相當於是拿個數不選,剩下的個數分到組內,如果我們加一個組來裝着個數,那麼這個組可能爲空,同時發現這個組和別的組不一樣,但是第二類斯特林數組之間是沒有標號區別的,所以我們就往空組加入一個(強行規定所在的組爲不選組),則非空和有區別這兩個限制都被滿足了,方案數就是。
但是其實這樣做很傻,直接類似第二類斯特林數,
後面那個中的就代表了不選這一選項,直接也沒有什麼問題。
#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
題意:爲在之間均勻隨機的離散變量,求不增的概率。
先離散化,將離散化後排序得到一個數組
設表示前個變量最後一個變量的概率。
那麼
後面那個組合數就是在中選擇個可以相同的數但是需要無序的方案。(因爲最後分配給的變量是有序的。)
注意這個轉移必須要都包含這個區間纔行。
#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
給出,求走步(每步可以讓橫座標或縱座標)的方案數使得途中不經過距離原點曼哈頓距離爲的點。
容斥,設表示走了步之後到達了號禁止點(禁止點爲距離原點曼哈頓距離爲的點集),至少一共走過了個禁止點。
所以答案就是
注意到我們直接計算容斥係數可以不需要這一維。
所以在我們可以直接計算兩個點之間走步到達的方案數後,
複雜度就是的。
設一個點在另一個點在,一共走步要到達。
發現因爲有相反的方向(減少的方向)所以計數很困難,考慮如何用上一共走步的條件。
如果我們把減少橫座標看做增加縱座標,減少縱座標看做增加橫座標,
那麼橫座標的增加量比縱座標的增加量大,總步數又是,所以我們可以得出增加縱座標的次數是,方案數爲
但是這樣無法區分增加縱座標和減少橫座標這兩種操作(等),
於是我們再反着定義:
把減少橫座標看做增加橫座標,減少縱座標看做增加橫座標,增加橫座標看做增加縱座標,增加縱座標不變。
那麼縱座標的增加量比橫座標的增加量大。
方案數爲
然後發現這樣定義,兩個組合數的乘積就是我們需要的答案。
如果我們記橫座標之差爲,縱座標之差爲的話答案就是:
這個方法感覺有點奇怪,特別是拓展到三維的時候他是四個組合數乘起來。
但是他可以。
#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
有概率選來異或你手上的數,問第一次得到的期望時間。
怎麼感覺ZJOI2019開關就是把這道題的FWT拆了拆式子快速實現。
發現多次到達同一個數很難解決,但是發現如果倒過來我們求從第一次到的時間並且讓不能轉移出來就可以解決多次到達同一個數的問題,因爲這樣同一個數都變成了,便於統一處理。
所以設表示從第一次到的時間。
則有,注意這個式子是對進行轉移,所以。
寫成生成函數就是
注意不是由上面的方程得到的,而是因爲的和爲,所以卷積之後所有項的和不變,所以解出來的。
那麼我們將變成則可以發現。
直接寫除法即可,注意到也就是說我們後被除的式子中這項爲(因爲別的都不爲),注意到這東西表達的方程是,我們考慮就這樣讓,發現這個方程可能會無解,因爲我們加多了條件,但是沒關係,我們解方程是用解,不會考慮無解。
之後如果得到了,考慮怎麼得到,假設真實的。
那麼在之後,因爲
所以每個位置都減去即可。
#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
給出,求的方案數,其中是中的一個,
活生生猜出來的解法
因爲要,所以假設說一個方案中各有個,那麼和他只是順序不同的方案數有種。
由庫默爾定理我們可以知道一個二進制位只能由一個所擁有,這樣就可以解決的問題,把的每個二進制位拿出來即可。(有個更牛逼的結論,在意義下)
但是。
考慮,所以當我們在考慮第位的時候,剩餘的後面都無法讓變爲。
所以我們從大到小枚舉,用保存位,然後轉移,的時候需要枚舉每個位來轉移,時間複雜度
#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
求個的排列的映射,對於所有都存在,其中或
設
則我們讓
這樣可以組合出來以內所有奇數,包括負的。
證明可以考慮歸納證明。
如果是奇數,那麼正負兩邊已經能讓我們到達所有位置。
如果是四的倍數,可以構造排列來讓我們可以使得一個位置移動奇數或偶數,注意這時這個排列是可以讓的奇偶性不同的,所以這個排列就不需要了(剛好卡進),我們只需要找距離最近也就是的那邊走。
如果是,可以構造排列 和即可。
#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
題意:給出個不同的的數,當滿足都未被刪去,並且時可以將刪去,求能刪除最多數的刪除序列數。
將視作的連邊,則我們對於每個弱連通圖分別計算方案。
(注意下文的討論中圖是弱聯通的。)
顯然這個如果有邊,則有邊。
所以如果在某次刪除中需要找到一個,這個一定可以沒有入度。
我們把沒有入度的點集看做,其他點看做。
則我們需要求最少刪到還有多少點,換個方向考慮,假如一開始我們讓一些點存在,然後讓這些,存在,不存在的拓展出存在,如果能拓展出所有點,那麼這個拓展方案反過來就和合法的刪點方案一一對應。
首先中的點不可能被刪,所以中的點一開始都是存在的,刪最多的點意味着中一開始存在的點要儘量少,最少爲,接下來我們給出構造的方案使得中一開始的點數爲。
對於中存在的點,找能夠到達他的所有的中的點,那麼能到達的點都會變爲存在,再重複這個過程即可,容易發現弱連通圖中的所有點都會變爲存在,也就是中任意一個點開始我們都可以讓所有點存在。
考慮如何根據這個過程構造出刪點方案,我們定義一種新的標記,這種標記只會打在的點中,對於中一開始的點,我們找能夠到達他的所有的中的點,給打上標記,之後我們找下一個被刪除的點,這個只需要保證能夠到達他的所有的中的點存在一個,是有標記的即可,接下來給能夠到達他的所有的中的點打上標記,如此重複即可得到一個刪點方案。
可以證明中的點數,首先可以認爲中的點都應該,否則沒有出邊也沒有入邊不滿足強聯通,對於的所有點,中的點構成了一個反鏈,其大小的最小鏈覆蓋,我們可以給出一個鏈覆蓋爲等形如一個奇數的條鏈,所以中的點數。
於是我們用一個狀壓,表示目前集合的點打上了標記,已經刪去了個點,
注意到我們不應該把中的點是否被選過納入狀態,所以我們需要有兩種轉移。
一種是刪去能到達他的只有中的點,那麼第一維不變,第二維,需要預處理出能到達他的只有中的點數,通過統計出還沒被刪的點數來做決策。
第二種是刪去能使變大的點並且這個點能被到達,這個點顯然不會被刪過,所以我們可以有一個的。
#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);
}