Codeforces Round #621 (Div. 1 + Div. 2)E(二分查找,枚举分界点,容斥原理)
Posted ldudxy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #621 (Div. 1 + Div. 2)E(二分查找,枚举分界点,容斥原理)相关的知识,希望对你有一定的参考价值。
可以把每头牛看作一个位置,有几对牛可以放置相当于有几对位置可以给它睡觉,没有牛可以在其他牛的位置睡觉,所以有几对牛放置的可能答案就乘多少(相当于在原本的两个集合里分别插入一个元素,元素代表它睡觉的位置)
1 #define HAVE_STRUCT_TIMESPEC 2 #include<bits/stdc++.h> 3 using namespace std; 4 int a[5007]; 5 vector<int>v[5007]; 6 int l[5007],r[5007]; 7 const long long mod =1e9+7; 8 int n,m; 9 int binary_search_(int color,int num){ 10 int point=upper_bound(v[color].begin(),v[color].end(),num)-v[color].begin(); 11 return point; 12 } 13 pair<int,long long> solve(){ 14 long long sum=1; 15 int ans=0; 16 for(int i=1;i<=n;++i){//枚举草的颜色 17 int x=binary_search_(i,l[i]);//有x头牛可以放在左边 18 int y=binary_search_(i,r[i]);//有y有牛可以放在右边 19 if(x*y-min(x,y)>0){//两边都可以有牛放置 20 ans+=2; 21 sum=(sum*(x*y-min(x,y)))%mod;//每头牛停止的位置是唯一的(数据保证不存在喜爱相同颜色且数量也相同的牛) 22 //有几对喜爱吃颜色i草的牛,答案就乘多少,可以把每头牛看作一个位置,有几对牛可以放置相当于有几对位置可以给它睡觉,没有牛可以在其他牛的位置睡觉,所以有几对牛放置的可能答案就乘多少(相当于在原本的两个集合里分别插入一个元素,元素代表它睡觉的位置) 23 } 24 else if(x||y){//只有一边可以有牛放置或者两边只能放置同一头牛 25 ++ans; 26 sum=(sum*(x+y))%mod;//两种情况对答案产生的影响都是x+y,前者x或y有一个是0,后者x和y都是1 27 } 28 } 29 return make_pair(ans,sum); 30 } 31 int main(){ 32 ios::sync_with_stdio(false); 33 cin.tie(NULL); 34 cout.tie(NULL); 35 cin>>n>>m; 36 for(int i=1;i<=n;++i){ 37 cin>>a[i]; 38 ++r[a[i]];//[i,n]区间里a[i]出现的次数 39 } 40 for(int i=1;i<=m;++i){ 41 int x,y; 42 cin>>x>>y; 43 v[x].emplace_back(y); 44 } 45 for(int i=1;i<=n;++i) 46 sort(v[i].begin(),v[i].end()); 47 int ans=0; 48 long long num=0; 49 for(int i=0;i<=n;++i){//以i+0.5为分界点 50 --r[a[i]];//分界点以右a[i]出现次数-1(a[i]被划到了左边) 51 ++l[a[i]];//分界点以左a[i]出现次数+1(a[i]被划到了左边) 52 pair<int,long long>pr=solve(); 53 if(pr.first>ans){ 54 ans=pr.first; 55 num=pr.second; 56 } 57 else if(pr.first==ans) 58 num=(num+pr.second)%mod; 59 } 60 for(int i=0;i<=n;++i){ 61 l[a[i]]=0;//初始化 62 ++r[a[i]];//回归枚举分界线以前的状态 63 } 64 /*举一个重复计算的例子 65 8 2 66 1 1 1 1 2 2 2 2 67 1 2 68 2 2 69 答案应输出2 3 70 不容斥的话会输出2 11 71 因为当一次枚举分界点的时候i=2~7,牛的最多头数都是2,可是排列方式总数全都加到了sum中 72 通过容斥可以在第二次枚举分界点的时候i=2~6把多余的方案数去掉 73 容斥去掉的是实际上被重复计算的那些,可能有多个区间计算了同样的方案,相同方案只计算一次,不同方案还是都应该计入贡献 74 */ 75 for(int i=0;i<n;++i){//第一次枚举分界点的时候计算了很多重复情况,需要用容斥将它挤掉 76 //新的分界左边区间是[0,i],右边区间是[i+2,n] 77 --r[a[i+1]]; 78 ++l[a[i]]; 79 pair<int,long long>pr=solve(); 80 if(pr.first==ans) 81 num=(mod+num-pr.second)%mod;//去掉重复计算的次数 82 } 83 cout<<ans<<" "<<num; 84 return 0; 85 }
以上是关于Codeforces Round #621 (Div. 1 + Div. 2)E(二分查找,枚举分界点,容斥原理)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #621 (Div. 1 + Div. 2) 题解
Codeforces Round #621 (Div. 1 + Div. 2)D dij(思维)
Codeforces Round #621 (Div. 1 + Div. 2)A-C简单记录
Codeforces Round #621 (Div. 1 + Div. 2) D