[atAGC013F]Two Faced Cards

先对$c_{i}$离散到$[0,n]$上,并令$a_{i},b_{i},d_{i},e_{i}$对应到第一个大于等于他的数

考虑若$a_{n+1}$和$b_{n+1}$也已经确定如何做:

有一个$o(2^{n})$的暴力,即暴力确定每一个数是选择$a_{i}$还是$b_{i}$,记这些数依次为$p_{i}$,贪心去匹配,即将两者从小到大排序后有$p_{i}\le c_{i}$

事实上,可以通过以下方式来判定:对于一个初始为0的数组,对所有$[p_{i},n]$加1,对所有$[c_{i},n]$减1,合法当且仅当所有位置都非负

证明:若$p_{i}\le c_{i}$,必然非负,同时若存在非负的位置$x$,即$\sum[x\ge p_{i}]-[x\ge c_{i}]<0$,对两者排序后,有$p_{i}中第一个大于x的位置<c_{i}中第一个大于x的位置$,记前者为$k$,则$p_{k}>x\ge c_{k}$

接下来,先全部令$p_{i}=a_{i}$,此时会有若干个位置小于0,然后可以对$[b_{i},a_{i})$这个区间加1,即表示令$p_{i}=$改为选择$b_{i}$,选择尽量少的区间使得最终非负(特别的,若$b_{i}>a_{i}$一定不会选择,删去此类区间)

同样可以贪心,即对于$k=\max_{a_{i}<0}i$(若不存在即结束),必然选择包含$k$且左端点最小的区间,重复此过程,通过set维护最小左端点即可得到最优解,复杂度为$o(qn\log_{2}n)$

证明:对$i$之后的位置加1没有意义,因此右端点可以对$i$取min,两个相互包含的区间取较大的区间更优(另外不一定要保证在左端点最小的同时右端点最大)

接下来,由于仅有$n+1$变化,不妨$o(2)$去枚举最后一个点的选择,之后相当于询问对某一个后缀全部加1后的答案,也可以看作这个后缀要求大于等于-1即可

类似的贪心,即对于$k=\max_{a_{i}<-1}i$,必然选择包含$k$左端点最小的区间(不论对什么后缀加1)

证明:假设对$[x,n]$这个后缀加1,若$x\le k$则$k=\max_{a_{i}<0}i$,因此必然修改;若$x>k$则$k$之后至多只有一个操作包含$k$,因为对于这样一个操作以后$k$之后所有数都变得非负了,此时$a_{k}<0$,因此仍要对$k$修改

(这里并不一定选择左端点最小的,因为可能上次包含$k$的操作就是左端点最小的,但必然是选择的)

同样重复这个过程,选出必选的区间,此时所有位置都大于等于-1

此时我们从左到右,对$a_{i}=-1$的位置求出仅考虑$i$以前(包括$i$)的所有-1,那么必然选择包含$i$且左端点最小的区间,然后变成了一个前缀的子问题,记录一下答案即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 100005
 4 vector<int>v[N];
 5 multiset<pair<int,int> >s;
 6 int n,m,x,y,tot,a[N],b[N],c[N],f[N],ans[N];
 7 bool cmp(int x,int y){
 8     return x>y;
 9 }
10 int find(int k){
11     return lower_bound(c,c+n+1,k)-c;
12 }
13 void update(int l,int r,int p){
14     f[r]+=p;
15     if (l)f[l-1]-=p;
16 }
17 int main(){
18     scanf("%d",&n);
19     for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
20     for(int i=0;i<=n;i++)scanf("%d",&c[i]);
21     scanf("%d",&m);
22     sort(c,c+n+1);
23     for(int i=0;i<=n;i++)update(find(c[i]),n,-1);
24     for(int i=1;i<=n;i++){
25         b[i]=min(b[i],a[i]);
26         if (b[i]>c[n]){
27             for(int i=1;i<=m;i++)printf("-1\n");
28             return 0;
29         }
30         if (a[i]>c[n]){
31             update(find(b[i]),n,1);
32             tot++;
33             continue;
34         }
35         a[i]=find(a[i]);
36         b[i]=find(b[i]);
37         update(a[i],n,1);
38         if (b[i]<a[i])v[a[i]-1].push_back(b[i]);
39     }
40     for(int i=0;i<=n;i++)sort(v[i].begin(),v[i].end(),cmp);
41     int sum=0;
42     for(int i=n;i>=0;i--){
43         sum+=f[i];
44         for(int j=0;j<v[i].size();j++)s.insert(make_pair(v[i][j],i));
45         while (sum<-1){
46             if ((!s.size())||((*s.begin()).first>i)){
47                 for(int i=1;i<=m;i++)printf("-1\n");
48                 return 0;
49             }
50             int k=(*s.begin()).second;
51             s.erase(s.begin());
52             tot++;
53             update(v[k].back(),k,1);
54             v[k].pop_back();
55             sum++;
56         }
57     }
58     for(int i=n;i;i--)f[i-1]+=f[i];
59     for(int i=0;i<=n;i++)v[i].clear();
60     while (!s.empty()){
61         v[(*s.begin()).first].push_back((*s.begin()).second);
62         s.erase(s.begin());
63     }
64     for(int i=0;i<=n;i++){
65         for(int j=0;j<v[i].size();j++)s.insert(make_pair(i,v[i][j]));
66         if (f[i]>=0)ans[i]=ans[i-1];
67         else{
68             if (!s.size()){
69                 ans[i]=0x3f3f3f3f;
70                 continue;
71             }
72             int k=(*s.begin()).first;
73             if (!k)ans[i]=1;
74             else ans[i]=ans[k-1]+1;
75         }
76         while ((s.size())&&((*s.begin()).second<=i))s.erase(s.begin());
77     }
78     for(int i=1;i<=m;i++){
79         scanf("%d%d",&x,&y);
80         int k=-1;
81         if (x<=c[n]){
82             if (x<=c[0])k=max(k,n-tot+1);
83             else k=max(k,n-(ans[find(x)-1]+tot)+1);
84         }
85         if (y<=c[n]){
86             if (y<=c[0])k=max(k,n-tot);
87             else k=max(k,n-(ans[find(y)-1]+tot));
88         }
89         printf("%d\n",k);
90     }
91 }
View Code

 

posted @ 2020-12-30 09:19  PYWBKTDA  阅读(142)  评论(0编辑  收藏  举报