18年山东省赛-Four-tuples【容斥定理】

15 篇文章 0 订阅
13 篇文章 0 订阅

题目链接:http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Index/problemdetail/pid/4219.html

 

读错题目,一开始以为是任意两个元素互不相等。写了好长实际后才发现是x1​≠x2​,x2​≠x3​,x3​≠x4​,x4​≠x1。

思路:用全集减去非法集合的并。

非法集合有四个

A:x1=x2  B:x2=x3 C x3=x4 D x4=x1

ans = |U|-|A∪B∪C∪D|

其中|A∪B∪C∪D|可以用容斥进行计算,可以对某一状态进行二进制编码后计算。

例如

     x4  x3  x2  x1     编码

A    0   0    1    1      3

B    0   1    1    0      6

C    1   1    0    0     12

D    1   0    0    1      9

AUB这个状态可以用 3|6 来表示,表示x3=x2且x2=x1时的方案数。

但是需要注意的是AUC这个状态不能用3|12来表示,AUC的含义是x1=x2且x3=x4,而3|12的含义是x1=x2=x3=x4。

另外就是任意三个集合的与四个集合的交相等,可以在计算的时候简化一些。

#include <bits/stdc++.h>
#define ll long long
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define pii pair<int,int>
#define all(x) (x).begin(),(x).end()
#define mp make_pair
#define pb push_back
using namespace std;
const int mod = 1e9+7;
pii s[5];
ll ans = 1;
void work(int x,int flag) {
    ll l = 0,r = 1e9;
    int now = 0;
    ll last = 1;
    int T = 4;
    while(T--) {
        now++;
        if(x&1) {
            l = max(l,1ll*s[now].first);
            r = min(r,1ll*s[now].second);
        }
        else last = last*(s[now].second-s[now].first+1)%mod;
        x >>= 1;
    }
    if(r<l) return;
    ans = (ans+flag*(last*(r-l+1))%mod+mod)%mod;
}
int main() {
    //freopen("a.txt","r",stdin);
    ios::sync_with_stdio(0);
    int T;
    cin>>T;
    while(T--) {
        ans = 1;
        rep(i, 1, 4) {
            cin>>s[i].first>>s[i].second;
            ans = ans *(s[i].second-s[i].first+1) %mod;
        }
        work(3,-1);work(6,-1);work(12,-1);work(9,-1);
        work(3|6,1);work(3|9,1);work(6|12,1);work(12|9,1);
        work(15,-1);work(15,-1);work(15,-1);
        ll l1 = max(s[1].first,s[2].first);
        ll r1 = min(s[1].second,s[2].second);
        ll l2 = max(s[3].first,s[4].first);
        ll r2 = min(s[3].second,s[4].second);
        if(r1>=l1&&r2>=l2) ans = (ans+ (r1-l1+1)*(r2-l2+1))%mod;
        l1 = max(s[2].first,s[3].first);
        r1 = min(s[2].second,s[3].second);
        l2 = max(s[1].first,s[4].first);
        r2 = min(s[1].second,s[4].second);
        if(r1>=l1&&r2>=l2) ans = (ans+ (r1-l1+1)*(r2-l2+1))%mod;
        cout<<ans<<endl;
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值