Codeforces Round #832 (Div. 2) E

牛逼题。

转化:注意到 \(a_i+b_i\not =a_{i-1}+b_{i-1}\),移项,\(a_i-a_{i-1}\not =b_{i-1}-b_i\),左边恒 \(\ge 0\),右边恒 \(\le 0\),显然二者只要不同时取等就好了,其他无所谓。你考虑用 2 个网格图表示,\(A(i,j)\) 表示 a 第 \(i\) 个选 \(j\),b 同理。然后你发现不能同时取等的限制,第一维完全没用,只需要 2 个值压一起,就一定能满足。所以你就转完了。

通过拐点刻画路径,这样每条路径的贡献方式唯一,你只要钦定拐点都选即可刻画唯一的路径,然后路径上的其他点随便选。

https://codeforc.es/contest/1747/problem/E

https://www.luogu.com.cn/blog/340163/cf1747-sol

本质上是啥:为了便于计数,对于点集的本质不同转化到路径,钦定一个确定的点集仅有一种唯一的路径转化方式,然后再计数。这也解释了为啥你钦定的拐点必须选,因为你钦定出来的这一条路径,当且仅当你选完所有的拐点的时候,才能刻画出来这条路径。

关于拐点的方案。

image

#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define PW(x) ((x)>=0?pw[(x)]:0)
using namespace std;
const int mod=(int)(1e9+7),N=(int)(1e7+5);
int jie[N],djie[N],pw[N];
int fpow(int x,int y) {
	if(y<0) return 0;
	int res=1; x%=mod;
	while(y) {
		if(y&1) res=res*x%mod;
		y>>=1; x=x*x%mod;
	} return res;
}

int C(int n,int m) {
	if(n<0||m<0||m>n) return 0;
	return jie[n]*djie[m]%mod*djie[n-m]%mod;
}
int n,m;
void sol() {
	cin>>n>>m;
	int ans=0;
	for(int i=0;i<=n+m;i++) {
		int s=n+m+1-i-2;
		if(s<0) break ;
		int qwq=0;
//		for(int j=i;j<=i;j++) {
		qwq=(qwq+(PW(s)*(i+2)%mod+s*PW(s-1)%mod)%mod)%mod;
//		}
		ans=(ans+qwq*C(n,i)%mod*C(m,i)%mod)%mod; 
	}
//	cout<<ans<<'\n';
	ans=(ans%mod+mod)%mod;
	cout<<ans<<'\n';
}

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	jie[0]=djie[0]=1; for(int i=1;i<=N-5;i++) jie[i]=jie[i-1]*i%mod;
	djie[N-5]=fpow(jie[N-5],mod-2); for(int i=N-5;i>=1;i--) djie[i-1]=djie[i]*i%mod;
	pw[0]=1; for(int i=1;i<=N-5;i++) pw[i]=pw[i-1]*2%mod;
	int T; cin>>T; while(T--) sol();
	return 0;
} 
posted @ 2022-11-05 13:12  FxorG  阅读(153)  评论(5编辑  收藏  举报