这是一道很好的容斥原理题。
首先这里推荐两道类似的题,spojRNG(1667是它的离散版本),还有bzoj3129(两题容斥部分很类似)
我们首先把第一类区间分为(-INF,L - 1] 与 (-INF,R],把第二类区间分为[L,INF) 与 [R + 1,INF) 那么我们简单写写,把前面的值设为a - x,后面的值设为 b + y。
那么甲胜要求的就是
∑i(a−x)>∑j(b+y)
,移项以后就可以发现是一堆数的和小于一个定值,这时候就要求个组合数
C(n,k)
,其中k很小,所以不用预处理再算,而是直接算(我好傻,MLE了一发)。
那么每种情况的方案数知道了,只要暴力容斥就可以了,我写了个二进制的,很慢,早知道写dfs了,还有代码也极丑,因为调调改改,也没写个函数啥的(本来以为可以写很短的,就直接在main里面写)。
代码:
//
// main.cpp
// 1667
//
//
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int T;
int k1,k2;
const int maxn = 10;
int L1[maxn],R1[maxn],L2[maxn],R2[maxn];
typedef long long ll;
const ll mod = 1e9 + 7;
ll pow_mod(ll a,ll n){
ll ret = 1;
while(n > 0){
if(n & 1) ret = ret * a % mod;
a = a * a % mod;
n >>= 1;
}
return ret;
}
ll C(int n,int m){
ll ret = 1;
for(ll i = n;i > n - m;i--){
ret = ret * i % mod;
}
//cout << 1 << endl;
for(ll i = 1;i <= (ll) m;i++){
ret = ret * pow_mod(i,mod - 2) % mod;
}
return ret;
}
int L3[maxn],L4[maxn];
int R3[maxn],R4[maxn];
int main(int argc, const char * argv[]) {
cin >> T;
while(T--){
cin >> k1;
for(int i = 1;i <= k1;i++){
scanf("%d%d",&L1[i],&R1[i]);
L4[i] = L1[i];
R4[i] = R1[i];
L1[i]--;
R4[i]++;
}
cin >> k2;
for(int i = 1;i <= k2;i++){
scanf("%d%d",&L2[i],&R2[i]);
L3[i] = L2[i];
R3[i] = R2[i];
R2[i]++;
L3[i]--;
}
int n = k1 + k2;
ll ans = 0;
ll ans1 = 0;
ll ans2 = 0;
//cout << 1 << endl;
for(int i = 0;i < (1 << n);i++){
int sum = 0;
int sum1 = 0;
int num = 0;
int num1 = 0;
for(int j = 1;j <= k1;j++){
if(i & (1 << (j - 1))){
sum += L1[j];
sum1 += L1[j];
num++;
num1++;
}
else{
sum1 += R1[j];
sum += R1[j];
}
}
for(int j = k1 + 1;j <= n;j++){
if(i & (1 << (j - 1))){
sum -= R2[j - k1];
sum1 -= R2[j - k1];
num++;
num1++;
}else{
sum -= L2[j - k1];
sum1 -= L2[j - k1];
}
}
//cout << 1 << endl;
if(sum >= 0){
ll tmp = C(sum + n - 1,n - 1);
if(num & 1)ans = (ans - tmp % mod + mod) % mod;
else ans = (ans + tmp) % mod;
}
//cout << 1 << endl;
if(sum1 > 0){
ll tmp = C(sum1 - 1 + n,n);
if(num1 & 1) ans1 = (ans1 - tmp % mod + mod) % mod;
else ans1 = (ans1 + tmp) % mod;
}
int num2 = 0;
int sum2 = 0;
for(int j = 1;j <= k2;j++){
if(i & (1 << (j - 1))){
sum2 += L3[j];
num2++;
}
else{
sum2 += R3[j];
}
}
for(int j = k2 + 1;j <= n;j++){
if(i & (1 << (j - 1))){
sum2 -= R4[j - k2];
num2++;
}else{
sum2 -= L4[j - k2];
}
}
if(sum2 > 0){
ll tmp = C(sum2 - 1 + n,n);
if(num2 & 1) ans2 = (ans2 - tmp % mod + mod) % mod;
else ans2 = (ans2 + tmp) % mod;
}
}
ll all = 1;
for(int i = 1;i <= k1;i++){
all = all * (long long)(R1[i] - L1[i]) % mod;
}
for(int i = 1;i <= k2;i++){
all = all * (long long)(R2[i] - L2[i]) % mod;
}
all = pow_mod(all, mod - 2);
ans = ans * all % mod;
ans1 = ans1 * all % mod;
ans2 = ans2 * all % mod;
//ll ans3 = (all - (ans + ans1) % mod + mod) % mod;
//ans3 = ans3 * all % mod;
cout << ans1 << " " << ans << " " << ans2 << endl;
}
return 0;
}
截个图纪念一下吧,第一个640分题。