AGC056 題解


更好的閱讀體驗

Fun fact: For all AGC problems with score>=1600 this year, the number of accepted solutions is no more than 1. --Retired_MiFaFaOvO

A. Three Cells per Row and Column

  • \(n \times n\) 的方格黑白染色,使其滿足:
    • 每行每列均恰有 \(3\) 個黑格。
    • 整個圖中恰有 \(n\) 個黑色四聯通塊。
  • \(6 \leq n \leq 500\)

最簡單構造題?

不考慮第二個限制,可以這樣染:

#.....##
##.....#
###.....
.###....
...###..
....###.
.....###

對於第二個限制,考慮換一下行的順序。

可以想到讓每一行都形成一個聯通塊(前兩行共同組成 \(2\) 個聯通塊),所以就是:

#.....##
##.....#
..###...
.....###
.###....
....###.
###.....
...###..

\(n=6\)\(n=7\) 時會有點小問題,實測把第 \(2\) 行和第 \(4\) 行換一下即可。


代碼里是按列寫的。

#include<bits/stdc++.h>
using namespace std;
const int N=550;
int n,ans[N][N];

int main(){
	cin>>n;
	ans[1][1]=ans[1][2]=ans[2][2]=1;
	ans[n][1]=ans[n][2]=ans[n-1][1]=1;
	int nw=2;
	for(int st=3;st>=1;st--){
		for(int x=st;x+2<=n;x+=3){
			nw++;
			ans[x][nw]=ans[x+1][nw]=ans[x+2][nw]=1;
		}
	}
	if(n==6 || n==7){
		for(int i=1;i<=n;i++) swap(ans[i][2],ans[i][4]);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(ans[i][j]) putchar('#');
			else putchar('.');
		}
		putchar('\n');
	}
}

B. Range Argmax

  • 給定 \(M\) 個區間 \(\{(L_i,R_i)\}\)。求長度為 \(M\) 的數組 \(x_1,x_2,\cdots,x_m\) 的數量,滿足:
    • 存在長度為 \(N\) 的排列 \(p_1,\cdots,p_N\),滿足 \(\forall i, p_{x_i}=\max\limits_{j=L_i}^{R_i} \{p_j\}\)
  • 答案對 \(998244353\) 取模。
  • \(2 \leq N \leq 300\)\(1 \leq M \leq N(N-1)/2\)\(1 \leq L_i < R_i \leq N\),所有區間互不相同。

場上想了兩小時沒整出來

考慮直接數 \(x\),要求等價於若 \(i\)\(j\) 區間分別選了 \(a\)\(b\) 作為最大值且 \(a,b \in [L_i,R_i] \wedge [L_j,R_j]\),那么 \(a=b\)然后就不會了

轉換思路,考慮數 \(p\),但是為了避免算重我們讓一個 \(x\) 只對應一個 \(p\)

考慮怎樣由 \(x\) 構造一個 \(p\),可以從大到小插入每個值,將其插入能插入的 最左端 的位置。

這樣構造並沒有什么性質能夠快速判斷 \(x\) 能否生成 \(p\)。但是我們發現每次插入一個值后,跨過該位置的區間的最大值已經被定下來了,而左右區間是獨立的,可以考慮區間 DP。

對於區間 \([l,r]\),枚舉它的最大值位置為 \(k\),即將它分成了 \([l,k)\)\((k,r]\) 兩部分。然而,為了確保不算重,我們需要保證 \(k\) 不能放在更左邊的位置

  • 對於右邊的區間,沒有限制
  • 對於左邊的區間,若左邊的區間選擇了 \(t\),那么必須有一個區間同時包含 \(t\)\(k\),否則 \(k\) 就可以放在 \(t\) 而不改變了 \(x\) 數組了。

於是設 \(f_{l,r,k}\) 表示區間 \([l,r]\) 的最大值位置為 \(k\) 時的答案,可以枚舉左區間最大值位置 \(t\) 轉移,時間復雜度 \(O(n^4)\)

至於判斷是否有區間同時包含 \(t\)\(k\),也可以用一些高超的寫法做到 \(O(n^3)\)

使用前綴和優化,總時間復雜度 \(O(n^3)\)

所以你管這叫 B(900) ???


#include<bits/stdc++.h>
using namespace std;
const int N=330,mod=998244353;
int n,m,ans;
int G[N][N],sG[N][N],pre[N][N],f[N][N][N],sf[N][N][N];

int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int l,r;
		cin>>l>>r;
		G[l][r]=1;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			sG[i][j]=sG[i][j-1]+G[i][j];
	for(int len=1;len<=n;len++){
		for(int l=1;l+len-1<=n;l++){
			int r=l+len-1,p=l;
			for(int k=l;k<=r;k++){
				int tot=(k==r)+(sf[k+1][r][r]+mod-sf[k+1][r][k])%mod;
				
				while(p<=r && sG[p][r]-sG[p][k-1]==0) p++;
				
				if(p<k) f[l][r][k]=(f[l][r][k]+(sf[l][k-1][k-1]+mod-sf[l][k-1][p-1])%mod)%mod;
				if(k==l) f[l][r][k]++;
				
				f[l][r][k]=1ll*f[l][r][k]*tot%mod;
				sf[l][r][k]=(sf[l][r][k-1]+f[l][r][k])%mod;
			}
		}
	}
	cout<<sf[1][n][n];
}

C. 01 Balanced

  • 給出 \(M\) 個區間 \(\{[L_i,R_i]\}\),求字典序最小的長度為 \(N\) 的 01 串 \(s\),滿足給出的所有區間內 \(0\)\(1\) 的數量相等。
  • \(2 \leq N \leq 10^6\)\(1 \leq M \leq 2 \times 10^5\)\(1 \leq L_i < R_i \leq N\)\(2 \mid R_i-L_i+1\),所有區間互不相同。

因為不會差分約束而沒有切掉

考慮構造一種前綴和 \(S_i=S_{i-1}+(-1)^{s_i}\)。則題目限制等價於 \(S_{L_i-1}=S_{R,i}\)

另一方面,限制是 \(|S_i-S_{i-1}|=1,i \in [1,N]\)

考慮差分約束,將第二個限制視為 \(|S_i-S_{i-1}| \leq 1\),從 \(0\) 開始跑最短路。

由於區間長度為偶數且跑的是最短路,不可能出現 \(S_i=S_{i-1}\) 的情況。

由於邊權只有 \(0\)\(1\),可以跑 Dijkstra,甚至 BFS 都行,時間復雜度 \(O(N+M)\)


#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5,M=2e6+5;
int n,m,s;

struct nod{
	int to,nxt,w;
}e[M*2];
int head[N],cnt;

void add(int u,int v,int w){
	e[++cnt].to=v;
	e[cnt].w=w;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

int dis[N];
bool vis[N];

struct abc{
	int num,dis;
};
bool operator <(abc x,abc y){
	return x.dis>y.dis;
}
priority_queue <abc> q;

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		add(i-1+1,i+1,1);
		add(i+1,i-1+1,1);
	}
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		u--;
		add(u+1,v+1,0);
		add(v+1,u+1,0);
	}
	
	memset(dis,0x7f,sizeof(dis));
	dis[1]=0;
	q.push((abc){1,0});
	while(!q.empty()){
		abc tmp=q.top();
		q.pop();
		int u=tmp.num;
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(!vis[v] && dis[u]+e[i].w<dis[v])
				dis[v]=dis[u]+e[i].w,q.push((abc){v,dis[v]});
		}
	}
	for(int i=2;i<=n+1;i++){
		if(dis[i]>dis[i-1]) putchar('0');
		else putchar('1');
	}
}

D. Subset Sum Game

  • 黑板上有 \(N\) 個數字 \(A_1,\cdots,A_N\) 和兩個數 \(L,R\)
  • Alice 和 Bob 在玩游戲,Alice 先手。每次操作是擦去黑板上的一個數字。
  • \(\frac{N}{2}\) 輪后,若 Alice 擦去的數之和 \(s\) 滿足 \(L \leq s \leq R\),Alice 獲勝;否則 Bob 獲勝。
  • 若兩人均采取最優策略,問最后誰會獲勝。
  • \(2 \leq N \leq 5000\)\(2 \mid N\)\(1 \leq A_i \leq 10^9\)\(0 \leq L \leq R \leq \sum A_i\)

還不是很懂結論和證明……

先考慮轉化條件。設 \(S=\sum A_i\)\(sA,sB\) 分別代表兩人所取走的數之和。

\(sA-sB=sA-(S-sA)=2sA-S\)\(sA=\dfrac{1}{2}(sA-sB+S)\)

\(L \leq sA \leq R\) 等價於 \(L \leq \dfrac{1}{2}(sA-sB+S) \leq R\),即 \(2L-S \leq sA-sB \leq 2R-S\)

再繼續簡化,設 \(X=L+R-S\),則上等價於 \(|(sA-sB)-X| \leq R-L\)

問題等價於:

有一個變量 \(x\) 初始為 \(L+R-S\),Alice 可以選擇一個 \(A_i\)\(x\) 加上,而 Bob 可以選擇一個 \(A_i\)\(x\) 減掉。Alice 想最大化 \(|X|\) 而 Bob 相反,求 \(x\) 最后的值。

有結論:

對於所有 \(p\),將 \(p,p+X,A_1,\cdots,A_N\) 從小到大排序為 \(a_1,\cdots,a_{N+2}\),則 \(x\) 最后的值即為 \((a_2-a_1)+(a_4-a_3)+\cdots+(a_{N+2}-a_{N+1})\) 的最小值。

暴力枚舉 \(p\) 顯然是不行的,但是若 \(p\)\(p+X\) 均落在兩個區間中間顯然可以左右移動變得更小,所以只需考慮 \(p\)\(p+X\)\(A_t\) 中的某個即可。

時間復雜度 \(O(N^2 \log N)\)


#include<bits/stdc++.h>
using namespace std;
const long long N=5500,INF=1e18;
long long n,L,R,S,X,anss=INF;
long long a[N];
long long b[N],id;

void solve(long long x){
	id=0;
	if(x<a[1]) b[++id]=x;
	if(x+X<a[1]) b[++id]=x+X;
	for(long long i=1;i<=n;i++){
		b[++id]=a[i];
		if(a[i]<=x && x<a[i+1]) b[++id]=x;
		if(a[i]<=x+X && x+X<a[i+1]) b[++id]=x+X;
	}
	for(int i=1;i<id;i++) if(b[i]>b[i+1]) swap(b[i],b[i+1]);
	long long ans=0;
	for(long long i=1;i<=id;i+=2) ans+=b[i+1]-b[i];
	anss=min(anss,ans);
}

int main(){
	cin>>n>>L>>R;
	for(long long i=1;i<=n;i++) cin>>a[i],S+=a[i];
	sort(a+1,a+n+1);
	a[n+1]=INF;
	X=S-(L+R);
	for(long long i=1;i<=n;i++) solve(a[i]),solve(a[i]-X);
	if(anss<=R-L) puts("Alice");
	else puts("Bob");
}

E. Cheese

  • 有一個長度為 \(N\) 的圓周,設某點 \(O\) 位置為 \(0\),圓周上一點的位置定義為從 \(O\) 沿順時針到該點的距離。有 \(N\) 只老鼠分別站在 \(0.5,1.5,\cdots,N-0.5\) 的位置,有 \(N\) 個放奶酪的位置 \(0,1,\cdots,N-1\),位置 \(i\) 放奶酪的概率為 \(A_i\)
  • 每一次操作,按 \(A_i\) 概率隨機在某個位置放上一塊奶酪。這塊奶酪隨后會沿着圓周順時針游走,直到它被吃掉;當它遇到一只未吃過奶酪的老鼠時,有一半的概率被吃掉。
  • 對每只老鼠求出 \(N-1\) 次操作后,它還未吃到奶酪的概率,對 \(998244353\) 取模。
  • \(1 \leq N \leq 40\)

賽后直接切了,你管這叫 E(1600)?

發現放奶酪的順序似乎無關緊要。如果有一前一后兩個奶酪,我們可以不管順序,讓后面的奶酪先走到前面的奶酪處,再兩塊奶酪一起走。這樣顯然不影響結果(因為奶酪和老鼠間不區分)。

考慮計算最后老鼠 \(X\) 還未吃到奶酪的概率。

參考上面的思路,我們可以先把所有奶酪放好(最后要乘上可重排),再讓所有奶酪轉到 \(X\) 前面(這個過程中不會有奶酪經過 \(X\)),這時候只要知道還剩下多少奶酪(或老鼠)即可快速算出答案。

先考慮第一步。設 \(f_{i,j,k}\) 表示轉移到 \(i+X\) 位置,已經放了 \(j\) 塊奶酪,老鼠們已經吃掉放上去的 \(k\) 塊奶酪的概率,最后 \(f_{N,N-1,k}\) 即是需要的東西。

轉移需要分兩步(放奶酪,奶酪走一步讓老鼠吃)。

放奶酪(注意可重排):

\[f_{i,j+p,k} \leftarrow f_{i,j+p,k}+\dfrac{1}{p!} A_{i+k}^p f_{i,j,k} \]

吃奶酪:

\[f_{i+1,j,k} \leftarrow f_{i+1,j,k}+\dfrac{1}{2^{j-k}} f_{i,j,k} \]

\[f_{i+1,j,k+1} \leftarrow f_{i+1,j,k+1}+(1-\dfrac{1}{2^{j-k}}) f_{i,j,k} \]

單次時間復雜度 \(O(N^4)\)

再考慮第二步。假設現在還剩下 \(k-1\) 塊奶酪(都在 \(X\) 前面)和 \(k\) 只老鼠,我們要求最后 \(X\) 吃不到奶酪的概率。

打表發現答案是 \(\dfrac{1}{2^{k}-1}\),為什么呢?

將老鼠按順時針編號,\(X\) 編號為 \(0\)。設 \(p_i\) 表示編號為 \(i\) 的老鼠最后吃不到奶酪的概率。

只考慮老鼠 \(i\) 與老鼠 \(i+1\)\(i \in [0,k)\)),假設現在輪到 \(i\),還剩下 \(x\) 塊奶酪,有 \(4\) 種情況:

  • \(i\)\(i+1\) 都沒吃,區分不出來,不考慮(留到下一輪考慮)。
  • \(i\)\(i+1\) 都吃了,區分不出來,不考慮。
  • \(i\) 吃了但 \(i+1\) 沒吃,概率為 \((1-\dfrac{1}{2^x})\dfrac{1}{2^{x-1}}\)
  • \(i\) 沒吃但 \(i+1\) 吃了,概率為 \((1-\dfrac{1}{2^{x-1}})\dfrac{1}{2^x}\)

所以有 \(p_{i+1}:p_i=(1-\dfrac{1}{2^x})\dfrac{1}{2^{x-1}}:(1-\dfrac{1}{2^{x-1}})\dfrac{1}{2^x}=2:1\),即 \(p_{i+1}=2p_i\)

自然可得 \(p_i=2^i p_i\)\(i \in [0,k)\))。

又有 \(\sum p_i=1\),於是 \((2^k-1)p_0=1\),得 \(p_0=\dfrac{1}{2^k-1}\)。得證。

總時間復雜度 \(O(n^5)\)超好寫


#include<bits/stdc++.h>
using namespace std;
const long long N=44,mod=998244353,inv2=(mod+1)/2;
long long mul[N],inv[N],pw[N],ipw[N];
long long ksm(long long a,long long x){
	long long tot=1;
	while(x){
		if(x & 1ll) tot=1ll*tot*a%mod;
		a=1ll*a*a%mod;
		x>>=1ll;
	}
	return tot;
}

long long n,A[N],ans;
long long f[2][N][N],id;

int main(){
	cin>>n;
	for(long long i=0;i<n;i++) cin>>A[i],A[i]=1ll*A[i]*ksm(100,mod-2)%mod;
	
	mul[0]=inv[0]=1;
	for(long long i=1;i<=n;i++) mul[i]=mul[i-1]*i%mod;
	inv[n]=ksm(mul[n],mod-2);
	for(long long i=n-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
	
	pw[0]=ipw[0]=1;
	for(long long i=1;i<=n;i++) pw[i]=pw[i-1]*2%mod,ipw[i]=ipw[i-1]*inv2%mod;
	
	
	for(long long X=0;X<n;X++){
		memset(f,0,sizeof(f));
		id=0;
		f[0][0][0]=1;
		for(long long i=1;i<=n;i++){
			id^=1;
			memset(f[id],0,sizeof(f[id]));
			for(long long j=0;j<=n;j++){
				for(long long k=0;k<=n;k++){
					for(long long p=0;j+p<=n;p++)
						f[id][j+p][k]=(f[id][j+p][k]+inv[p]*ksm(A[(X+i)%n],p)%mod*f[id^1][j][k]%mod)%mod;
				}
			}
			
			if(i==n) break;
			id^=1;
			memset(f[id],0,sizeof(f[id]));
			
			for(long long j=0;j<=n;j++){
				for(long long k=0;k<=j;k++){
					f[id][j][k]=(f[id][j][k]+ipw[j-k]*f[id^1][j][k]%mod)%mod;
					f[id][j][k+1]=(f[id][j][k+1]+(1+mod-ipw[j-k])*f[id^1][j][k]%mod)%mod;
				}
			}
		}
		ans=0;
		for(long long i=0;i<=n;i++){
			ans=(ans+mul[n-1]*f[id][n-1][i]%mod*ksm(pw[n-i]-1,mod-2)%mod)%mod;
		}
		cout<<ans<<" ";
	}
}

F. Degree Sequence in DFS Order

  • 求所有長度為 \(N\) 的序列 \(a_1,\cdots,a_N\),滿足:
    • 存在一張 \(N\) 個點 \(M\) 條邊無自環(可重邊)的無標號無向圖,設其 DFS 序(點序列)之一為 \(d_1,\cdots,d_n\),滿足 \(a_i\)\(d_i\) 的度數。
  • 答案對 \(998244353\) 取模。
  • \(2 \leq N \leq M \leq 10^6\)

這比上場 F 陽間多了

(寫完后)陽間個鬼,這官方題解真不是一般的簡略,證明根本看不懂啊啊啊啊

Part 1

不妨按 DFS 序標號,即存在 DFS 序之一為 \(1,2,\cdots,N\)。(\(*\)

顯然必要條件是:對於所有 \(u \in [2,N]\),存在 \(v<u\) 使 \((u,v)\) 間連邊。即若現在已走過 \(1 \sim u\),可以走到 \(u+1\)。(\(**\)

但這並不是充分條件,因為 DFS 序會走到底,可能無法選擇 \(u+1\)

注意到若有 \(a<b<c<d\) 滿足 \((a,c)\)\((b,d)\) 連邊,可以改為 \((a,d)\)\((b,c)\) 連邊,這樣度數與性質均不變。

令邊 \((u,v)\) 的邊權為 \((v-u)^2\),則每次操作必然增大邊權和,故存在邊權和最大的終態無法繼續操作。

假設已走過 \(1 \sim u\)\(u\) 節點不與 \(u+1\) 相連。設 \(u+1\)\(v\) 相連,此時 \(1 < v < u < u+1\)\((1,u)\)\((v,u+1)\) 間連邊,矛盾。因此這個終態是滿足(\(*\))的。

這樣,每一個滿足(\(**\))的圖都可生成一個滿足(\(*\))的圖。我們只需要數滿足(\(**\))的圖的度數序列數量即可。


Part 2

我們來考慮 \(a\) 的限制。

首先顯然有 \(\sum\limits_{i=1}^N a_i=2M\)

更細節的,由於每個節點都向前面連邊,可以整體考慮前 \(u\) 個點:

  • \(\sum\limits_{i \leq u} a_i \geq 2(u-1)+1\)\(+1\) 是因為前 \(u\) 個點中至少一個要向 \(u+1\) 連邊)。(條件 \(1\)
  • 同時還有下限 \(a_{u+1} \leq M-(u-1)\)。(條件 \(2\)

這是必要條件,令人驚訝的是同時也是充分條件。證明如下:

考慮一輪貪心:對於每個點 \(2,3,\cdots,N\),從前面的點選出一個剩余度數最大的點連邊。(由於條件 \(1\),可以做到)

如果一輪后所有點剩余度數均不超過 \(M-(N-1)\),就可以貪心匹配。

假設有 \(a_i'>M-(N-1)\),那么由於 \(\sum a_t'=2M-2(N-1)\),不存在另外的 \(a_j' \geq M-(N-1)\)。由條件 \(2\)\(a_i \leq M-(i-2)\)。下面說明在貪心過程中,\(i\) 號點連接了 \(N-i+1\) 條邊(也是至多),則有 \(a_i' \leq M-(i-2)-(N-i+1)=M-(N-1)\),矛盾。

首先,在貪心到 \(i\) 時,\(i\) 一定會向前面連一條邊。然后,在貪心到 \(k>i\) 時,若存在剩余度數 \(a_{(now)j}>a_{(now)i}\),由於 \(a_i'\) 頂多減到 \(M-(N-1)-1\)\(a_j' \geq M-(N-1)\),這與假設矛盾。因此每一次都可以選擇 \(a_i\)\(a_i\) 連的總度數即為 \(N-i+1\)。證畢。


Part 3

現在真的要開始計數了!

先做些變換。令 \(b_0=a_1\)\(b_i=a_{i+1}-1,i \in [1,N)\)(注意所有 \(b_i\) 均為非負整數),則三個條件變為:

  • \[\sum\limits_{i=0}^{n-1} b_i=2M-(N-1) \]

  • \[\sum\limits_{i < u} b_i \geq u \]

  • \[b_u \leq M-u \]

這是一個類似卡特蘭數的東西,考慮對應到圖上。

如果不考慮第三個條件,相當於從原點出發,每次先向上走 \(b_0\),再右移一格,再向上走 \(b_1\),再右移一格……向上走 \(b_{N-1}\)。最后走到 \((N-1,2M-(N-1))\)。限制是不能越過 \(y=x\) 這條線。

用卡特蘭數的算法可以輕松算出是 \(\dbinom{2M}{N-1}-\dbinom{2M}{N-2}\)

考慮減去不滿足第三個條件的情況。這里又有一個性質:至多只存在一個 \(K\) 使 \(b_K > M-K\)。證明如下:

\(p<q\) 使 \(b_p > M-p\)\(b_q > M-q\)。由條件 \(2\)\(\sum\limits_{i<p} b_i \geq p\),所以 \(\sum\limits_{i=0}^{N-1} b_i \geq \sum\limits_{i<p} b_i+b_p+b_q \geq p+(M-p+1)+(M-q+1)=2M-(q-2) > 2M-(N-1)\),矛盾。

所以,加上第三個條件。我們欽定了 \(b_K \geq M-K+1\),為了維護 \(b_i\) 取非負整數,令 \(b_K \leftarrow b_K-(M-K+1)\),則 \(\sum\limits_{i=0}^{N-1} b_i=2M-(N-1)-(M-K+1)=M-N+K\)。現在的圖看起來是這樣的:

(圖來自官方題解

不妨將圖分為三角形與矩形上下兩部分來考慮。具體而言,考慮枚舉 \(x\),而第一次躍過 \(K\) 的點 \((x,y)\)(即 \(y \geq K\)),分成前后兩部分計數。方案數實際等於 \((0,0) \rightarrow (x,K-1)\) 乘上 \((x,K) \rightarrow (N-1,M-N+K)\)(想一想,為什么?)。

於是答案為(\(K=0\) 時與 \(K=1\) 答案相同,但是要特判):

\[Ans=\dbinom{2M}{N-1}-\dbinom{2M}{N-2}-\dbinom{M-1}{M-N}+\sum\limits_{k=1}^{N-1} \sum\limits_{x=0}^{k-1} \Big [\dbinom{x+k-1}{x}-\dbinom{x+k-1}{x-1} \Big] \dbinom{M-x-1}{M-N} \]

交換和號~

\[Ans=\dbinom{2M}{N-1}-\dbinom{2M}{N-2}-\dbinom{M-1}{M-N}+\sum\limits_{x=0}^{N-1} \dbinom{M-x-1}{M-N} \sum\limits_{k=x+1}^{N-1} \dbinom{x+k-1}{x}-\dbinom{x+k-1}{x-1} \]

眾所周知 \(\sum\limits_{i=0}^n \dbinom{i}{m}=\dbinom{n+1}{m+1}\),所以拆開得到:

\[Ans=\dbinom{2M}{N-1}-\dbinom{2M}{N-2}-\dbinom{M-1}{M-N}+\sum\limits_{x=0}^{N-1} \dbinom{M-x-1}{M-N} \Big[ \dbinom{x+n-1}{x+1}-\dbinom{2x-1}{x+1}-\dbinom{x+n-1}{x}+\dbinom{2x-1}{x}+[x=0]\Big ] \]

預處理組合數,時間復雜度 \(O(N+M)\)


這短短的三行代碼后是多少艱辛的嘗試與推導啊……

#include<bits/stdc++.h>
using namespace std;
const long long N=2e6+5,mod=998244353;
long long mul[N],inv[N];
long long ksm(long long a,long long x){
	long long tot=1;
	while(x){
		if(x & 1ll) tot=1ll*tot*a%mod;
		a=1ll*a*a%mod;
		x>>=1ll;
	}
	return tot;
}
void init(long long lim){
	mul[0]=inv[0]=1;
	for(long long i=1;i<=lim;i++) mul[i]=mul[i-1]*i%mod;
	inv[lim]=ksm(mul[lim],mod-2);
	for(long long i=lim-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
long long C(long long m,long long n){
	if(m<n || m<0 || n<0) return 0;
	return mul[m]*inv[n]%mod*inv[m-n]%mod;
}

long long n,m,ans;
int main(){
	init(N-5);
	cin>>n>>m;
	for(long long x=0;x<n;x++)
		ans=(ans+mod-(C(x+n-1,x+1)+mod-C(2*x-1,x+1)+mod-C(x+n-1,x)+C(2*x-1,x)+mod+(!x))%mod*C(m-x-1,m-n)%mod)%mod;
	cout<<(ans+C(2*m,n-1)+mod-C(2*m,n-2)+mod-C(m-1,m-n))%mod;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM