

還是有點蠢。。。
多測沒清空T3掛40。。。(只得了人口普查分20)
多測題要把樣例復制粘兩遍自測一下防止未清空出鍋。
然而不算分。。。
其實到現在了算不算也不重要了吧。。。
而且其實T3只考慮最長路上的點這個思路其實肯定都能想到啊。
懶得打。那么就分少啊。
對於分數,還是要貪婪一些的。
T1:你相信引力嗎
維護最大值關系:肉眼可見的單調棧/隊列。(剛開始認為是棧,后來其實發現是一個隊列)
環,經典套路,拆成兩倍長的序列。
維護一個單調不增的單調棧,根據題目含義畫畫圖,可以發現答案是:
對於當前點,從棧里第一個嚴格大於當前元素的值開始數,棧里有多少個元素。
暴力思路就是二分。
可以發現你找到的那個元素后面的部分都會被彈掉,所以一邊彈一邊計數即可。
特殊的一點是權值相同的,這部分不會被彈棧,但是對答案也有貢獻。
所以在每個元素入棧的時候記錄一下棧頂有幾個連續相同的元素即可。
然后還要考慮找到第一個比當前元素大的元素,所以檢查一下棧底的元素是否比當前元素大,如果大的話再累加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 }
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 }
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 }
