[考試反思]1106csp-s模擬測試103: 渺茫

7點以前上不了博客,用gedit寫的。諒解一下。數組


看起來不是特別慘?可是被sdfz爆踩了。。。
並且其實並不能說「不是特別慘」吧
90分算個啥啊?還湊不出個T2的AC
難易度評估錯誤,T2是最簡單的沒看出來。而後爆搜不夠優秀TLE 0.
T1僞證的60分只有20,若是外層加一個clock就有40.。。
T3部分分打滿,很明顯了卻沒有想到啓發式合併
說實在的仍是經驗不足實力不夠,面對難題原型畢露(雖然說相對於簡單題稍微好一些)

T1:Game
先不考慮字典序,如何獲得最高的積分?
貪心。這個好說。
可是在後面的操做中還須要修改牌序,致使局面變化,還要統計動態的積分。
就是說,你須要依次考慮每一位應該填什麼,而後刪除這張牌再check一下看積分變不變。
在積分不變的基礎上,這一位越大越好。這知足單調性能夠二分(稍後具體講)
其實這相似於單點修改操做(挺抽象的)。
暴力貪心的話那麼就須要從頭再從新作一遍。
考慮如何優化?那隻能是數據結構了。
線段樹分治。(基於值域)
對於A和B有的牌都開一個權值線段樹。考慮合併兩個子樹(即線段樹update)操做。
右兒子的A和左兒子的B能夠結合(知足大小關係),這些是能夠獲勝的局面,全局累加答案。
即設$win=min(A_{rc},B_{lc})$。那麼在上傳的時候,就有$ans+=win,A_p=A_{lc}+A_{rc}-win,B_p=B_{lc}+B_{rc}-win$
就是已經勝利的局面累加積分而且再也不參與之後的運算。最後全局ans的值就是最大積分。
這就是最優決策。已是用盡可能小的A去打敗B了,等價於貪心。
單點修改的話直接重置某一個下標的AB值,整條鏈都修改一下就好。
在修改的時候要注意全局的ans應該要刪除原來這個節點的貢獻再修改並update,不然積分就會重複計算。
如今說明一下它的單調性(證實二分):其實並非直接二分,也不是徹底的單調性。
先考慮若是你贏了這一位,那麼你用的值越大,得分可能越低(浪費了)
若是你輸了這一位,那麼你用的值大了,總得分可能也會下降(也是浪費)
若是你能夠在贏下這一位的基礎上保證總積分不變,那麼你就會贏下這一位(由於這樣的話字典序會更大)
因此二分的過程實際上是:檢查這一位能不能贏,若是能夠就在$[b_i+1,max]$二分,若是不能贏就在$[1,b_i]$二分
找到最大取值,在線段樹裏刪除,同時也要把B刪除。逐位考慮便可。
能夠拿multiset維護一下剩餘A的最大值做爲二分上屆。否則有可能被卡常(只有我被卡了。。。)
複雜度$O(nlog^2n)$
%%%Rock_B教我看懂標程
%%%yxs暴力踩暴正解(比正解還難寫還須要一大堆特殊性質,咱也不會打%%%就是了)數據結構

聽說建樹容易被卡常,傳參好像比建樹全局調用快一些。ide

 1 #include<cstdio>
 2 #include<set>
 3 using namespace std;
 4 multiset<int>S;
 5 int n,a[100005],b[100005],cl[400005],cr[400005],A[400005],B[400005];
 6 int cnta[100005],cntb[100005],ans,rans;
 7 void up(int p){
 8     int nw=min(B[p<<1],A[p<<1|1]);
 9     ans+=nw;A[p]=A[p<<1]+A[p<<1|1]-nw;B[p]=B[p<<1]+B[p<<1|1]-nw;
10 }
11 void build(int p,int l,int r){
12     cl[p]=l;cr[p]=r;
13     if(l==r){A[p]=cnta[l];B[p]=cntb[l];return;}
14     build(p<<1,l,l+r>>1);build(p<<1|1,(l+r>>1)+1,r);
15     up(p);
16 }
17 void modify(int p,int pos){
18     if(cl[p]==cr[p]){A[p]=cnta[pos];B[p]=cntb[pos];return;}
19     ans-=min(B[p<<1],A[p<<1|1]);
20     if(pos<=cr[p<<1])modify(p<<1,pos);
21     else modify(p<<1|1,pos);
22     up(p);
23 }
24 bool chk(int x,int p,int de){
25     cnta[x]--;modify(1,x);
26     int rA=ans+de;
27     cnta[x]++;modify(1,x);
28     return rA==rans;
29 }
30 int main(){
31     freopen("game.in","r",stdin);freopen("game.out","w",stdout);
32     scanf("%d",&n);
33     for(int i=1;i<=n;++i)scanf("%d",&b[i]),cntb[b[i]]++;
34     for(int i=1;i<=n;++i)scanf("%d",&a[i]),cnta[a[i]]++,S.insert(a[i]);
35     build(1,1,*(--S.end()));rans=ans;
36     for(int i=1;i<=n;++i){
37         cntb[b[i]]--;modify(1,b[i]);
38         int l=b[i]+1,r=*(--S.end()),ta=0;
39         while(l<=r)if(chk(l+r>>1,i,1))ta=l+r>>1,l=(l+r>>1)+1;else r=(l+r>>1)-1;
40         if(!ta){
41             int L=1,R=b[i];
42             while(L<=R)if(chk(L+R>>1,i,0))ta=L+R>>1,L=(L+R>>1)+1;else R=(L+R>>1)-1;
43         }else rans--;
44         cnta[ta]--,modify(1,ta);printf("%d ",ta);S.erase(S.find(ta));
45     }puts("");
46 }
View Code


T2:Time
貪心。不能直接作就要去發現特殊元素或特殊性質。
考場上一直覺得最大值是特殊元素而後就卡死了。(由於會對其餘元素產生影響)
其實是要考慮最小值。它最後必定在兩側,考慮它是往左仍是右移就行了。
若是一個數出現了屢次,那麼就依次考慮是最左邊的數往左移仍是最右邊的數往右移就行了。優化

 1 #include<cstdio>
 2 #include<vector>
 3 using namespace std;
 4 vector<int>v[100005];
 5 int n,x[100005],t[100005];long long ans;
 6 void add(int p,int w){for(;p<=n;p+=p&-p)t[p]+=w;}
 7 int ask(int p,int a=0){for(;p;p^=p&-p)a+=t[p];return a;}
 8 main(){
 9     freopen("time.in","r",stdin);freopen("time.out","w",stdout);
10     scanf("%d",&n);
11     for(int i=1;i<=n;++i)scanf("%d",&x[i]),v[x[i]].push_back(i),add(i,1);
12     for(int i=1;i<=100000;++i){
13         int h=0,t=v[i].size()-1;
14         while(h<=t){
15             int l=ask(v[i][h]-1),r=ask(n)-ask(v[i][t]);
16             if(l<r)ans+=l,add(v[i][h],-1),h++;
17             else ans+=r,add(v[i][t],-1),t--;
18         }
19     }printf("%lld\n",ans);
20 }
View Code


T3:Cover
首先看到「包含或不相交」就知道要建樹,有父子關係。建一個超級父節點控制[1,n]避免它是森林。ui

由於我腦子很清奇因此個人建樹比較詭異,全部區間(包括超級根節點)按照l爲第一關鍵字從小到大,r爲第二關鍵字從大到小排序。spa

這樣排序以後,你就獲得了這棵樹的dfs序,而後根據「dfs序下一個點的子樹是一段連續的區間」,用全局的單調指針遞歸建樹便可(詳見代碼,這個講很差)
而後嘗試dp。
由於我腦子比較清奇因此我並無像題解同樣設出那個dp而後再差分。
個人dp定義是f[i][j]表示對於i這個點,你給它「第」j次覆蓋的機會時所獲得的「額外」收益。
因此個人dp一設出來就是題解裏那個數組的差分。
考慮轉移,我給這個點一次覆蓋機會,那麼它的決策要麼是把機會下傳給全部兒子,要麼是就用來覆蓋本身這個區間
那麼$f[i][j]=\sum f[son][j]$。在這樣獲得f數組以後它是單調的(你會採起最優決策來讓它收益最大化,最優決策必定會在第一次被取出,以此類推)
因此f[p][1]>=f[p][2]>=f[p][3]...然而這樣並無考慮這個點自己。
因此在這個自己已經有序的數組裏插入$w_p$就行了。而後超級根節點的f數組前綴和一下就是答案。
那25的部分分就不須要維護有序數組,而是直接開桶,把「倒深度」放進去再作前綴和就行了。
由於除了葉節點之外,一個點的子樹大小必定大於自身大小,因此有覆蓋機會就下傳就行了。
給出暴力代碼。3d

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<vector>
 4 using namespace std;
 5 vector<long long>v[300005];
 6 struct P{
 7     int l,r,w,o;
 8     friend bool operator<(P a,P b){
 9         return a.l<b.l||(a.l==b.l&&a.r>b.r)||(a.l==b.l&&a.r==b.r&&a.o>b.o);
10     }
11 }ps[300005];
12 long long ans,Ans[300005];int n,m,sz[300005],l[300005],to[300005],cnt,fir[300005],ald=2,spj=1;
13 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;}
14 void dfs(int p){
15     for(int i=fir[p];i;i=l[i])dfs(to[i]),sz[p]=max(sz[p],sz[to[i]]);
16     sz[p]++;v[p].resize(sz[p]);
17     for(int i=fir[p];i;i=l[i])for(int j=0;j<sz[to[i]];++j)v[p][j]+=v[to[i]][j];
18     v[p][sz[p]-1]=ps[p].w;
19     sort(v[p].begin(),v[p].end());reverse(v[p].begin(),v[p].end());
20 }
21 void build(int p){while(ps[p].l<=ps[ald].l&&ps[ald].r<=ps[p].r)link(p,ald),ald++,build(ald-1);}
22 int DFS(int p){
23     int dep=1;
24     for(int i=fir[p];i;i=l[i])dep=max(dep,DFS(to[i])+1);
25     Ans[dep]+=ps[p].w;
26     return dep;
27 }
28 int main(){freopen("cover.in","r",stdin);freopen("cover.out","w",stdout);
29     scanf("%d%d",&n,&m);
30     for(int i=1;i<=m;++i)scanf("%d%d%d",&ps[i].l,&ps[i].r,&ps[i].w),ps[i].o=i,ps[i].r--;
31     m++;ps[m]=(P){1,n,0,m};
32     for(int i=2;i<m;++i)if(ps[i].w!=ps[1].w)spj=0;
33     sort(ps+1,ps+1+m);build(1);
34     if(spj){DFS(1);for(int i=1;i<m;++i)Ans[i]+=Ans[i-1],printf("%lld ",Ans[i]);return 0;}
35     dfs(1);
36     v[1].resize(m);
37     for(int i=1;i<m;++i)ans+=v[1][i-1],printf("%lld ",ans);puts("");
38 }
TLE70

若是用數組維護,時空都沒法接受,考慮用數據結構。
須要支持的操做:插入一個值,不斷取出最大值。(而後刪除,這樣次大值就變成了最大值,就能夠依次取出並加和了)
大根堆。優先隊列。
然而就這樣空間對了時間複雜度仍是有問題。
啓發式合併,把重兒子的隊列直接接過來,而後再與其它兒子合併。
複雜度$O(nlog^2n)$,並不知道是否是正解複雜度(爲何出了300000?)指針

update:我又害人了我寫掛了,複雜度寫成$O(n^2logn)$了。code

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<queue>
 4 using namespace std;
 5 priority_queue<long long>v[300005],r;
 6 struct P{
 7     int l,r,w;
 8     friend bool operator<(P a,P b){
 9         return a.l<b.l||(a.l==b.l&&a.r>b.r);
10     }
11 }ps[300005];
12 long long ans,Ans[300005];int n,m,sz[300005],l[300005],to[300005],cnt,fir[300005],ald=2,spj=1;
13 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;}
14 void dfs(int p){
15     for(int i=fir[p];i;i=l[i])dfs(to[i]),sz[p]=max(sz[p],sz[to[i]]);
16     sz[p]++;
17     for(int i=fir[p];i;i=l[i])if(sz[to[i]]+1==sz[p]){swap(v[p],v[to[i]]);to[i]=0;break;}
18     for(int i=fir[p];i;i=l[i])if(to[i]){
19         while(!v[to[i]].empty())r.push(v[p].top()+v[to[i]].top()),v[p].pop(),v[to[i]].pop();
20         swap(r,v[p]);while(!r.empty())v[p].push(r.top()),r.pop();
21     }v[p].push(ps[p].w);
22 }
23 void build(int p){while(ps[p].l<=ps[ald].l&&ps[ald].r<=ps[p].r)link(p,ald),ald++,build(ald-1);}
24 int main(){freopen("cover.in","r",stdin);freopen("cover.out","w",stdout);
25     scanf("%d%d",&n,&m);
26     for(int i=1;i<=m;++i)scanf("%d%d%d",&ps[i].l,&ps[i].r,&ps[i].w),ps[i].r--;
27     m++;ps[m]=(P){1,n,0};
28     sort(ps+1,ps+1+m);build(1);
29     dfs(1);
30     for(int i=1;i<=m;++i)v[1].push(0);
31     for(int i=1;i<m;++i)ans+=v[1].top(),v[1].pop(),printf("%lld ",ans);
32 }
錯的
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<queue>
 4 using namespace std;
 5 priority_queue<long long>v[300005],r;
 6 struct P{
 7     int l,r,w;
 8     friend bool operator<(P a,P b){
 9         return a.l<b.l||(a.l==b.l&&a.r>b.r);
10     }
11 }ps[300005];
12 long long ans,Ans[300005];int n,m,sz[300005],l[300005],to[300005],cnt,fir[300005],ald=2,spj=1;
13 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;}
14 void dfs(int p){
15     for(int i=fir[p];i;i=l[i])dfs(to[i]),sz[p]=max(sz[p],sz[to[i]]);
16     sz[p]++;
17     for(int i=fir[p];i;i=l[i])if(sz[to[i]]+1==sz[p]){swap(v[p],v[to[i]]);to[i]=0;break;}
18     for(int i=fir[p];i;i=l[i])if(to[i]){
19         while(!v[to[i]].empty())r.push(v[p].top()+v[to[i]].top()),v[p].pop(),v[to[i]].pop();
20         while(!r.empty())v[p].push(r.top()),r.pop();
21     }v[p].push(ps[p].w);
22 }
23 void build(int p){while(ps[p].l<=ps[ald].l&&ps[ald].r<=ps[p].r)link(p,ald),ald++,build(ald-1);}
24 int main(){freopen("cover.in","r",stdin);freopen("cover.out","w",stdout);
25     scanf("%d%d",&n,&m);
26     for(int i=1;i<=m;++i)scanf("%d%d%d",&ps[i].l,&ps[i].r,&ps[i].w),ps[i].r--;
27     m++;ps[m]=(P){1,n,0};
28     sort(ps+1,ps+1+m);build(1);
29     dfs(1);
30     for(int i=1;i<=m;++i)v[1].push(0);
31     for(int i=1;i<m;++i)ans+=v[1].top(),v[1].pop(),printf("%lld ",ans);
32 }
對的
相關文章
相關標籤/搜索