2021.8.5考试总结[NOIP模拟31]

暴力打满直接rk3?

T1 Game


想了一万种贪心和两万种$hack$。

可以先用最显然的贪心求出最高得分是多少。(从小到大用最小的大于$b_i$的$a$得分)

然后用一棵权值线段树维护值域内$a$和$b$的个数,发现两个儿子合并对答案的贡献就是$min(a_r,b_l)$。(要用大的$a$对小的$b$)于是最优分数可以直接在建树时算出。

对每个位置考虑放什么数可以令最优分数不下降,发现它是单调的。于是可以二分。具体是把$mid$值删去后检查一遍最优分数有无下降。

对$a_mid$是否大于$b_i$要分类讨论,如果$a_mid$大于$b_i$,检查时要将最终答案$+1$,因为当前位置会贡献$1$。

找到该位答案后把$a_mid$和$b_i$删掉,对最优答案$-1$,使它不会对之后的答案产生影响。

挺妙的,在线段树操作中自动修改最优分数。

$code:$

 1 #include<bits/stdc++.h>
 2 #define ld rt<<1
 3 #define rd (rt<<1)|1
 4 #define rin register signed
 5 using namespace std;
 6 const int NN=1e5+5;
 7 int n,a[NN],pre[NN],b[NN],ans[NN],an[NN],bota[NN],k,it,botb[NN],tk;
 8 inline int read(){
 9     int x=0,f=1; char ch=getchar();
10     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
11     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
12     return x*f;
13 }
14 inline void write(int x){
15     char ch[20]; int len=0;
16     if(x<0) putchar('-'), x=~x+1;
17     do{
18         ch[len++]=x%10+(1<<5)+(1<<4);
19         x/=10;
20     }while(x);
21     for(rin i=len-1;i>=0;--i) putchar(ch[i]);
22     return;
23 }
24 struct segment_tree{
25     int suma[NN<<2],sumb[NN<<2],l[NN<<2],r[NN<<2];
26     void pushup(int rt){
27         int tmp=min(suma[rd],sumb[ld]); tk+=tmp;
28         suma[rt]=suma[ld]+suma[rd]-tmp;
29         sumb[rt]=sumb[ld]+sumb[rd]-tmp;
30     }
31     void build(int rt,int opl,int opr){
32         l[rt]=opl; r[rt]=opr;
33         if(opl==opr){ 
34             suma[rt]=bota[opl]; 
35             sumb[rt]=botb[opl]; 
36             return; 
37         }
38         int mid=opl+opr>>1;
39         build(ld,opl,mid);
40         build(rd,mid+1,opr);
41         pushup(rt);
42     }
43     void update(int rt,int pos){
44         if(l[rt]==r[rt]){ 
45             suma[rt]=bota[pos]; 
46             sumb[rt]=botb[pos]; 
47             return; 
48         }
49         int mid=l[rt]+r[rt]>>1;
50         tk-=min(sumb[ld],suma[rd]);
51         if(pos<=mid) update(ld,pos);
52         else update(rd,pos);
53         pushup(rt);
54     }
55 }s;
56 bool check(int mid,int op){
57     bota[mid]--; s.update(1,mid);
58     int res=tk+op;
59     bota[mid]++; s.update(1,mid);
60     return res==k;
61 }
62 signed main(){
63     n=read(); rin atmp=0;
64     for(rin i=1;i<=n;++i){ b[i]=read(); botb[b[i]]++; }
65     for(rin i=1;i<=n;++i){ int t=read(); bota[t]++; it=max(it,t); }
66     s.build(1,1,it); k=tk;
67     for(rin i=1;i<=n;i++){
68         botb[b[i]]--; s.update(1,b[i]);
69         while(!bota[it]&&it) it--;
70         int l=b[i]+1,r=it,pos=0;
71         while(l<=r){
72             int mid=l+r>>1;
73             if(check(mid,1)) l=mid+1, pos=mid;
74             else r=mid-1;
75         }
76         if(!pos){
77             l=0; r=b[i];
78             while(l<=r){
79                 int mid=l+r>>1;
80                 if(check(mid,0)) l=mid+1, pos=mid;
81                 else r=mid-1;
82             }
83         }
84         else k--;
85         bota[pos]--; s.update(1,pos); ans[i]=pos;
86     }
87     for(int i=1;i<=n;i++) write(ans[i]), putchar(' ');
88     putchar('\n');
89     return 0;
90 }
T1

 

T2 Time


是个贪心。每次找到序列中的最小值,把它移到左或右端点,哪近移哪。貌似可以分治。

事实上每个点移到它成为最小值时端点对答案的贡献就是它左/有大于它数的个数。树状数组即可。

(到现在我也不知道求逆序对为什么假了

$code:$

 1 #include<bits/stdc++.h>
 2 #define ld rt<<1
 3 #define rd (rt<<1)|1
 4 using namespace std;
 5 const int NN=1e5+5;
 6 int n,p[NN],ans,c[NN],pre[NN],suf[NN];
 7 inline int read(){
 8     int x=0,f=1; char ch=getchar();
 9     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
10     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
11     return x*f;
12 }
13 inline void write(int x){
14     char ch[20]; int len=0;
15     if(x<0) putchar('-'), x=~x+1;
16     do{
17         ch[len++]=x%10+(1<<5)+(1<<4);
18         x/=10;
19     }while(x);
20     for(int i=len-1;i>=0;i--) putchar(ch[i]);
21     return;
22 }
23 inline int lowbit(int x){ return x&(-x); }
24 inline void insert(int pos){
25     while(pos<100000){
26         c[pos]++;
27         pos+=lowbit(pos);
28     }
29 }
30 inline int query(int pos){
31     int res=0;
32     while(pos){
33         res+=c[pos];
34         pos-=lowbit(pos);
35     }
36     return res;
37 }
38 signed main(){
39     n=read();
40     for(int i=1;i<=n;i++)
41         p[i]=read();
42     for(int i=1;i<=n;i++){
43         pre[i]=query(100000)-query(p[i]);
44         insert(p[i]);
45     }
46     memset(c,0,sizeof(c));
47     for(int i=n;i;i--){
48         suf[i]=query(100000)-query(p[i]);
49         insert(p[i]);
50     }
51     for(int i=1;i<=n;i++)
52         ans+=min(pre[i],suf[i]);
53     write(ans); putchar('\n');
54     return 0;
55 }
T2

 

T3 Cover


 

是道神题。首先区间不交或包含,可以转化成树上问题。

暴力$DP$挺简单的,定义$f_{i,j}$为$i$子树中最多覆盖$j$层,有:

$f_{i,j}=max(max(\sum f_{son,j-1}+w_i,\sum f_{son,j}),f_{i,j-1})$

 然后时空$O(n^2)$全炸了。

注意到很多计算是重复或无用的,而$f_{i,j}$在$i$相同时单调不减,可以直接维护差分。

而因为维护的是最优策略,可以发现差分值也单调不增,然后考虑$\sum f_{son,j-1}+w_i$什么时候能成为最优答案。

首先差分意义下,初始的$f_{i,j}=\sum f_{son,j}$。

那么有$f_{i,j}<f_{i,j-1}+w_i\Rightarrow w_i>f_{i,j}-f_{i,j-1}$,即$w_i$大于$f_{i,j}$与$f_{i,j-1}$的差分值。

于是可以直接用保证数据单调的数据结构(堆,$multiset$)维护差分值,子树合并后直接将$w_i$插入即可。

$code:$

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 #define pb push_back
 4 using namespace std;
 5 const int NN=3e5+5;
 6 int n,m,res;
 7 vector<int>son[NN];
 8 struct node{
 9     int l,r,a;
10     bool operator<(const node& s)const{ return l<s.l||(l==s.l&&r>s.r); }
11 }d[NN];
12 multiset<int>f[NN];
13 stack<int>stk;
14 inline int read(){
15     int x=0,f=1; char ch=getchar();
16     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
17     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
18     return x*f;
19 }
20 inline void write(int x){
21     char ch[20]; int len=0;
22     if(x<0) putchar('-'), x=~x+1;
23     do{
24         ch[len++]=x%10+(1<<5)+(1<<4);
25         x/=10;
26     }while(x);
27     for(int i=len-1;i>=0;i--) putchar(ch[i]);
28     return;
29 }
30 void merge(multiset<int> &a,multiset<int> &b){
31     if(a.size()<b.size()) swap(a,b);
32     vector<int>tmp;
33     for(auto x:b){ tmp.pb(x+*a.begin()); a.erase(a.begin()); }
34     for(auto t:tmp) a.insert(t); 
35 }
36 void dfs(int s){
37     for(auto to:son[s]){
38         dfs(to);
39         merge(f[s],f[to]);
40     }
41     f[s].insert(-d[s].a);
42 }
43 signed main(){
44     n=read(); m=read();
45     for(int i=1;i<=m;i++)
46         d[i].l=read(), d[i].r=read(), d[i].a=read();
47     sort(d+1,d+m+1);
48     stk.push(0);
49     for(int i=1;i<=m;i++){
50         while(stk.top()!=0&&d[stk.top()].r<d[i].r) stk.pop();
51         son[stk.top()].pb(i);
52         stk.push(i);
53     }
54     dfs(0);
55     for(int i=1;i<=m;i++){
56         if(f[0].size()) 
57             res-=*f[0].begin(), f[0].erase(f[0].begin());
58         write(res); putchar(' ');
59     } putchar('\n');
60     return 0;
61 }
T3

 

posted @ 2021-08-06 07:40  keen_z  阅读(57)  评论(0编辑  收藏  举报