zoukankan      html  css  js  c++  java
  • Codeforces Round #621 (Div. 1 + Div. 2)E(二分查找,枚举分界点,容斥原理)

    可以把每头牛看作一个位置,有几对牛可以放置相当于有几对位置可以给它睡觉,没有牛可以在其他牛的位置睡觉,所以有几对牛放置的可能答案就乘多少(相当于在原本的两个集合里分别插入一个元素,元素代表它睡觉的位置)

     容斥的时候,第二遍会把当前位置i+1这个点没有牛在睡觉的情况全部去掉,尝试写了不容斥,直接计算当前枚举位置i一定有牛睡觉的方法,发现巨麻烦无比,遂放弃55555

     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       容斥去掉第i个位置没有牛的情况,这样可以保证所有计算在内的情况必定有牛要在[0,n]枚举区间i位置时睡觉
    75     */
    76     for(int i=0;i<n;++i){//第一次枚举分界点的时候计算了很多重复情况,需要用容斥将它挤掉
    77         //新的分界左边区间是[0,i],右边区间是[i+2,n]
    78         --r[a[i+1]];
    79         ++l[a[i]];
    80         pair<int,long long>pr=solve();
    81         if(pr.first==ans)
    82             num=(mod+num-pr.second)%mod;//去掉重复计算的次数
    83     }
    84     cout<<ans<<" "<<num;
    85     return 0;
    86 }
    保持热爱 不懈努力 不试试看怎么知道会失败呢(划掉) 世上无难事 只要肯放弃(划掉)
  • 相关阅读:
    wzplayer,tlplayer正式支持扩展dlna协议
    ASP.NET导出Excel或Word文件格式
    .NET对象序列化:TimeSpan
    探讨.NET中的联合结构
    浅析ASP.NET应用Autofac获取页面服务
    ASP.NET封装JS文件到DLL中并在页面中调用
    MySQL和MongoDB设计实例对比
    .NET中的异步编程:使用F#简化异步编程
    ASP.NET MVC 2自定义验证
    ASP.NET Session丢失问题原因及解决方案
  • 原文地址:https://www.cnblogs.com/ldudxy/p/12329345.html
Copyright © 2011-2022 走看看