ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AGC056 题解

2021-12-10 22:33:29  阅读:243  来源: 互联网

标签:dbinom int 题解 奶酪 long leq AGC056 sum


更好的阅读体验

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;
}

标签:dbinom,int,题解,奶酪,long,leq,AGC056,sum
来源: https://www.cnblogs.com/Appleblue17/p/15674075.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有