BBQ Hard dp + 组合数学 + 建模

传送门

文章目录

题意:

n n n组物品,每组有 a i a_i ai个肉和 b i b_i bi个菜,你可以选择两组物品让后将肉和菜其串在一根串上,问有多少种不同的串法。
两种方法不同当且仅当选的物品不同或者串的顺序存在至少一个位置一个方法串的肉,另一个串的菜。
n ≤ 2 e 5 , a i , b i ≤ 2 e 3 n\le2e5,a_i,b_i\le2e3 n2e5,ai,bi2e3

思路:

一个比较巧妙的建模题。

考虑暴力做法,显然答案为 ∑ i = 1 n ∑ j = i + 1 n ( a i + b i + a j + b j a i + a j ) \sum_{i=1}^{n}\sum_{j=i+1}^n\binom{a_i+b_i+a_j+b_j}{a_i+a_j} i=1nj=i+1n(ai+ajai+bi+aj+bj),但是这个是 n 2 n^2 n2的,而且基本没什么能优化的地方。
看到 a i , b i a_i,b_i ai,bi很小,可以考虑从这里入手。
枚举 a a a?也是行不通的。。
还是观察一下式子吧,将其抽象一下,考虑到平面两个点 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) (x1,y1),(x2,y2),从第一个点到第二个点的路径方案数为 ( x 2 − x 1 + y 2 − y 1 x 2 − x 1 ) \binom{x_2-x_1+y_2-y_1}{x_2-x_1} (x2x1x2x1+y2y1)
嗯?!这两个式子是不是像极了!
我们只需要将其中一个 ( a i , b i ) (a_i,b_i) (ai,bi)变成负数就行了!
所以我们要求的东西变成什么了?
不就是从 ∑ j = i + 1 n p a t h ( ( − a j , − b j ) , ( a i , b i ) ) \sum_{j=i+1}^npath((-a_j,-b_j),(a_i,b_i)) j=i+1npath((aj,bj),(ai,bi))的方案数嘛。
这个好算啊,直接转换成二维平面的 d p dp dp,让后 f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i ] [ j − 1 ] f[i][j]=f[i-1][j]+f[i][j-1] f[i][j]=f[i1][j]+f[i][j1]
但是你会发现一个问题,这个是求的 ∑ j = 1 n p a t h ( ( − a j , − b j ) , ( a i , b i ) ) \sum_{j=1}^npath((-a_j,-b_j),(a_i,b_i)) j=1npath((aj,bj),(ai,bi)),但是你要求 ∑ j = i + 1 n p a t h ( ( − a j , − b j ) , ( a i , b i ) ) \sum_{j=i+1}^npath((-a_j,-b_j),(a_i,b_i)) j=i+1npath((aj,bj),(ai,bi)),显然我们简单容斥一下,将原来的式子 ∑ i = 1 n ∑ j = i + 1 n ( a i + b i + a j + b j a i + a j ) = ( ∑ i = 1 n ∑ j = 1 n ( a i + b i + a j + b j a i + a j ) − ∑ i = 1 n ( 2 ∗ ( a i + b i ) 2 ∗ a i ) ) 2 \sum_{i=1}^{n}\sum_{j=i+1}^n\binom{a_i+b_i+a_j+b_j}{a_i+a_j}=\frac{(\sum_{i=1}^n\sum_{j=1}^n\binom{a_i+b_i+a_j+b_j}{a_i+a_j}-\sum_{i=1}^n\binom{2*(a_i+b_i)}{2*a_i})}{2} i=1nj=i+1n(ai+ajai+bi+aj+bj)=2(i=1nj=1n(ai+ajai+bi+aj+bj)i=1n(2ai2(ai+bi)))
这样问题就解决啦。

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;

//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N=2000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n;
int a[N],b[N];
LL f[4010][4010];
LL inv[N],fun[N];

LL C(int a,int b)
{
	if(a<0||b<0||a<b) return 0;
    return fun[a]*inv[b]%mod*inv[a-b]%mod;
}

LL qmi(LL a,LL b)
{
    LL ans=1%mod;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans%mod;
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

	inv[1]=1;
	fun[0]=fun[1]=1;
	for(int i=2;i<N;i++)
	    fun[i]=fun[i-1]*i%mod;
	inv[N-1]=qmi(fun[N-1],mod-2);
	for(int i=N-2;i>=0;i--)
	    inv[i]=(inv[i+1]*(i+1))%mod;
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]),f[2001-a[i]][2001-b[i]]++;
	for(int i=1;i<=4005;i++) {
		for(int j=1;j<=4005;j++) {
			(f[i][j]+=f[i-1][j])%=mod;
			(f[i][j]+=f[i][j-1])%=mod;
		}
	}
	
	LL ans=0;
	for(int i=1;i<=n;i++) {
		ans+=f[2001+a[i]][2001+b[i]]; ans%=mod;
		ans-=C(2*(a[i]+b[i]),2*a[i]); ans%=mod;
		ans+=mod; ans%=mod;
	}
	cout<<ans*qmi(2,mod-2)%mod<<endl;
	
	
	


	return 0;
}
/*

*/









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值