[考試反思]1105csp-s模擬測試102: 貪婪

仍是有點蠢。。。html

多測沒清空T3掛40。。。(只得了人口普查分20)c++

多測題要把樣例複製粘兩遍自測一下防止未清空出鍋。數據結構

然而不算分。。。ide

其實到如今了算不算也不重要了吧。。。spa

並且其實T3只考慮最長路上的點這個思路其實確定都能想到啊。3d

懶得打。那麼就分少啊。調試

對於分數,仍是要貪婪一些的。code

 

T1:你相信引力嗎htm

維護最大值關係:肉眼可見的單調棧/隊列。(剛開始認爲是棧,後來其實發現是一個隊列)blog

環,經典套路,拆成兩倍長的序列。

維護一個單調不增的單調棧,根據題目含義畫畫圖,能夠發現答案是:

對於當前點,從棧裏第一個嚴格大於當前元素的值開始數,棧裏有多少個元素。

暴力思路就是二分。

能夠發現你找到的那個元素後面的部分都會被彈掉,因此一邊彈一邊計數便可。

特殊的一點是權值相同的,這部分不會被彈棧,可是對答案也有貢獻。

因此在每一個元素入棧的時候記錄一下棧頂有幾個連續相同的元素便可。

而後還要考慮找到第一個比當前元素大的元素,因此檢查一下棧底的元素是否比當前元素大,若是大的話再累加1個答案。

而後還要考慮環拉成序列之後同一個位置的元素可能在棧裏出現2次。

因此就不是棧了,是個隊列。若是隊首元素的下表就是當前的下標,那麼就彈出。

彈出的時候要考慮它對「棧裏連續相同元素數量」的影響。

還有一個問題,若是對於一對冰錐,它的優弧和劣弧都知足條件,那麼它的答案會被計算兩次。

若是最大值出現了k次,那麼它對答案產生的多出的貢獻就是$\frac{k(k-1)}{2}$

特別的,當k=1時,若是次大值出現了j次,那麼多出的答案就是$j$

因此把多出的貢獻減去就是最終答案。

細節較多。可是若是上述分類討論都注意到了的話調試仍是挺簡單的。

 1 #include<cstdio>
 2 int read(){
 3     register int p=0;register char ch=getchar();
 4     while(ch<'0'||ch>'9')ch=getchar();
 5     while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar();
 6     return p;
 7 }
 8 int n,a[5000005],q[10000005],p[10000005],s[10000005],h=1,t,mx,se,tmx,tse;long long ans;
 9 int main(){
10     freopen("jolyne.in","r",stdin);freopen("jolyne.out","w",stdout);
11     n=read();
12     for(int i=1;i<=n;++i)a[i]=read();
13     for(int i=1;i<=n;++i)
14         if(a[i]>mx)se=mx,tse=tmx,mx=a[i],tmx=1;
15         else if(a[i]==mx)tmx++;
16         else if(a[i]>se)se=a[i],tse=1;
17         else if(a[i]==se)tse++;
18     for(int i=1;i<=n;++i){
19         while(t>=h&&a[i]>q[t])t--;
20         if(t>=h&&q[t]==a[i])s[++t]=s[t-1]+1;else s[++t]=1;
21         q[t]=a[i];p[t]=i;
22     }
23     for(int i=1;i<=n;++i){int nt=0;
24         while(t>=h&&p[h]<=i)nt=a[i]==q[h],h++;
25         while(t>=h&&a[i]>q[t])t--,ans++;
26         if(t>=h&&q[t]==a[i])s[++t]=s[t-1]+1;else s[++t]=1;
27         q[t]=a[i];p[t]=n+i;
28         if(h==t||q[h]!=q[t])nt=0;
29         s[t]-=nt;ans+=s[t]-1;if(q[h]!=a[i])ans++;
30 //        for(int j=h;j<=t;++j)printf("%3d ",q[j]);puts("");
31 //        for(int j=h;j<=t;++j)printf("%3d ",s[j]);puts("");
32 //        printf("%lld\n",ans);
33     }
34     if(tmx==1)ans-=tse;
35     else ans-=tmx*(tmx-1ll)/2;
36     printf("%lld\n",ans);
37 }
View Code

 

T2:停不下來的團長奧爾加

比較明顯的線性遞推。

能夠根據每一個點的出度/入度平衡來作。

出度分爲去右邊的和去左邊的,入度也是。

而後就能夠遞推了。

 1 #include<cstdio>
 2 #define mod 1000000007
 3 int n,to[1000005];long long ans,oR[1000005],oL[1000005],iL[1000005],iR[1000005];
 4 int main(){
 5     freopen("rideon.in","r",stdin);
 6     freopen("rideon.out","w",stdout);
 7     scanf("%d",&n);
 8     for(int i=1;i<=n;++i)scanf("%d",&to[i]);
 9     iL[n+1]=1;
10     for(int i=n;i;--i){
11         oR[i]=iL[i+1];
12         oL[i]=oR[i];
13         iR[to[i]]=(iR[to[i]]+oL[i])%mod;
14         iL[i]=(oR[i]+oL[i]-iR[i]+mod)%mod;
15         ans=(ans+iL[i]+iR[i])%mod;
16     }printf("%lld\n",ans);
17 }
View Code

 

T3:Lesson5!

這題爲何要多測啊啊啊?必定要記得清空否則調到死。

建超級源匯會好作一些。(不建也能夠,區別不大)

先預處理出來源點到每一個點的距離f,以及每一個點到匯點的距離g。

正反拓撲獲得。

而後對於每一條邊,通過它的最長路是fx+gy+1。稱之爲邊的權值。

對於刪除一個點,那麼全部與它有關的邊都不能貢獻答案。

因此再一個正向拓撲,分層考慮全部點。

對於每個點,刪除全部入邊的權值的貢獻(就是從數據結構裏刪除),此時出邊的貢獻尚未統計。

所謂的貢獻答案,其實就是在全部可行的最長路中取出最大的那一個,是一個維護了多個最長路決策的數據結構。

(因此說題解說的好像多麼玄乎同樣)

先不考慮這個數據結構是什麼,假設咱們能應付它須要的全部操做。

因此其實當前的狀態下,就至關與刪除了這個點時的狀態,因此此時的最長路就是刪掉這個點的最長路。(就是從數據結構裏找到最大值)

這個點考慮完以後就能夠把它的出邊都貢獻答案了。(就是放入數據結構)

由於你會在進入這一層的時候刪除,走出這一層的時候加入,那麼其實它只會在本層到下一層的時間存在。

進入下一層的時候表明這條最長路的邊就再也不是這一條了而是在這條最長路上的下一條。因此必定會不重不漏的最優決策。

怎麼求出答案?須要一個能夠求最大值,能夠刪除的數據結構。

multiset。(因而我開始嘲笑出題人不會STL還要寫權值線段樹)

而後我被卡常了。。。

可是我堅定不打數據結構!因此就上priority_queue亂搞。

加一個懶惰刪除便可。

不創建超級源匯的話,根據含義考慮,其實少的邊就是從每個點到其匯點的貢獻,因此在拓撲開始以前直接把每一個點到它的匯點的答案放進數據結構裏就行了。

還有我感受mikufun的思路很好,附個連接(雖然skyh沒臉又爆跳父親AC了而後纔開始長臉從新改了一遍(再長臉臉該多大了啊。。。))

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 priority_queue<int>Q;
 4 vector<int>v[100005];
 5 int n,m,dp[100005],ans,ansp,cnt,fir[100005],l[700005],to[700005],deg[100005];
 6 int f[100005],g[100005],q[100005],X[700005],Y[700005],delcnt[100005];
 7 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;deg[b]++;}
 8 int main(){
 9     freopen("johnny.in","r",stdin);
10     freopen("johnny.out","w",stdout);
11     int T;scanf("%d",&T);
12     while(T--){
13         scanf("%d%d",&n,&m);
14         for(int i=0;i<=n+1;++i)fir[i]=f[i]=g[i]=0,v[i].clear();
15         while(!Q.empty())Q.pop();
16         ans=1234567890;cnt=0;
17         for(int i=1;i<=m;++i)scanf("%d%d",&X[i],&Y[i]),link(X[i],Y[i]);
18         for(int i=m+1;i<=m+n;++i)X[i]=0,Y[i]=i-m,link(X[i],Y[i]);
19         m+=n;
20         for(int i=m+1;i<=m+n;++i)X[i]=i-m,Y[i]=n+1,link(X[i],Y[i]);
21         m+=n;n++;
22         int t=0;
23         for(int i=0;i<=n;++i)if(!deg[i])q[++t]=i;
24         for(int h=1;h<=t;++h){
25             int p=q[h];
26             for(int i=fir[p];i;i=l[i]){
27                 deg[to[i]]--;f[to[i]]=max(f[to[i]],f[p]+1);
28                 if(!deg[to[i]])q[++t]=to[i];
29             }
30         }
31         t=0;for(int i=0;i<=n;++i)fir[i]=0;cnt=0;
32         for(int i=1;i<=m;++i)link(Y[i],X[i]);
33         for(int i=0;i<=n;++i)if(!deg[i])q[++t]=i;
34         for(int h=1;h<=t;++h){
35             int p=q[h];
36             for(int i=fir[p];i;i=l[i]){
37                 deg[to[i]]--;g[to[i]]=max(g[to[i]],g[p]+1);
38                 if(!deg[to[i]])q[++t]=to[i];
39             }
40         }
41         t=0;for(int i=0;i<=n;++i)fir[i]=0;cnt=0;
42         for(int i=1;i<=m;++i)link(X[i],Y[i]),v[Y[i]].push_back(1+g[Y[i]]+f[X[i]]);
43         for(int i=0;i<=n;++i)if(!deg[i])q[++t]=i;
44         Q.push(2);
45         for(int h=1;h<=t;++h){
46             int p=q[h];
47             for(int i=0;i<v[p].size();++i)delcnt[v[p][i]]++;
48             while(delcnt[Q.top()])delcnt[Q.top()]--,Q.pop();
49             if(Q.top()<ans&&p&&p!=n)ans=Q.top(),ansp=p;
50             if(Q.top()==ans&&p<ansp&&p)ansp=p;
51             for(int i=fir[p];i;i=l[i]){
52                 deg[to[i]]--;Q.push(1+f[p]+g[to[i]]);
53                 if(!deg[to[i]])q[++t]=to[i];
54             }
55         }
56         printf("%d %d\n",ansp,ans-2);
57     }
58 }
60M的讀入量沒寫快讀竟然也過了。。。
相關文章
相關標籤/搜索