本來說好的好一場爛一場。
那樣的日子結束了,連着爛了兩場。。。
幸虧T3傻逼了救我一命不算太慘。。。
T1樹上的特殊性質會做但是沒有繼續想下去就死在錯貪心上了還沒有過那個點。。。
T2迭代至穩定被我錯誤的hack了,貪心炸了。
T3我覺得考場上我在yy證明,但是它是對的。
於是我去了趟廁所清醒了一下,回來還是hack不掉我的證明。。。
於是去隔壁問教練w的范圍,不告訴。。。
那我要打高精???
不可能的我又不是正解,於是開個long long就交了。
然后竟然就A了???
據說開int會飛天。
T1:Graph
盡量多的邊配對。。。
樹上的就可以簡單貪心。
但是變成圖之后多了些橫叉邊,然而並沒有什么影響。
照樣配對就可以了,特殊處理一下樹邊。
對於問題性質的擴展能力還是有所欠缺啊。。。

1 #include<cstdio> 2 #include<vector> 3 using namespace std; 4 int n,m,ec=1,fir[100005],al[100005],l[400005],to[400005],alt; 5 int ansx[200005],ansp[200005],ansy[200005]; 6 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 7 int dfs(int p,int fa){//printf("->%d %d\n",p,fa); 8 al[p]=1;int lst=0; 9 for(int i=fir[p];i;i=l[i])if(!al[to[i]]){ 10 if(!dfs(to[i],p))continue; 11 if(lst)++alt,ansx[alt]=lst,ansp[alt]=p,ansy[alt]=to[i],lst=0; 12 else lst=to[i]; 13 }else if(al[to[i]]&&to[i]!=fa&&to[i]>p) 14 if(lst)++alt,ansx[alt]=lst,ansp[alt]=p,ansy[alt]=to[i],lst=0; 15 else lst=to[i]; 16 if(fa&&lst){++alt,ansx[alt]=lst,ansp[alt]=p,ansy[alt]=fa,lst=0;return 0;} 17 return 1; 18 } 19 int main(){ 20 scanf("%d%d",&n,&m); 21 for(int x,y,i=1;i<=m;++i) scanf("%d%d",&x,&y),link(x,y),link(y,x); 22 for(int i=1;i<=n;++i) if(!al[i]) dfs(i,0); 23 printf("%d\n",alt); 24 for(int i=1;i<=alt;++i)printf("%d %d %d\n",ansx[i],ansp[i],ansy[i]); 25 }
思路積累:
- 性質由樹向圖上擴展
- 貪心:正確性證明
T2:Permutation/Wide Swap
類似於《菜餚制作》,用到了它的結論:
正向字典序最小,等同於下標與數值互換后的數組的逆向字典序最大。(逆向就是指reverse一下)
所以現在問題變成了構造最大的下標與數值互換的數組(叫pos吧)的逆向字典序最大。
注意下面這個結論並不是恆成立的(盡管據說本題成立但是沒有被證明):
正向字典序最大等同於原數組逆向字典序最小
現在考慮如何構造吧。
忘掉原數組,現在我們的任務就是構造pos。
現在的互換規則就是相鄰兩位的差至少為k。
那么因為是相鄰位交換,所以如果兩個數差不足k那么它們永遠不能互相跨越即交換相對位置。
那么對於所有i<j,posi與posj的差的絕對值小於k時就建上邊表示不能跨越。
那么就可以得到一個DAG了,跑一遍拓撲就可以得到合法的構造方案。
但是邊太多了考慮優化。
因為每個數支配的都是一段連續的區間,所以有些區間存在間接支配關系。
倒序考慮pos串,找到下標在合法區間內下標最相近的點建邊即可。

1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 using namespace std; 5 priority_queue<int>q; 6 int a[500005],b[500005],cl[2000005],cr[2000005],mn[2000005],n,k; 7 int fir[500005],l[1000005],to[1000005],cnt,deg[500005]; 8 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;deg[b]++;} 9 void build(int p,int l,int r){ 10 cl[p]=l;cr[p]=r;mn[p]=1234567; 11 if(l==r)return; 12 build(p<<1,l,l+r>>1);build(p<<1|1,(l+r>>1)+1,r); 13 } 14 void set(int p,int pos,int w){ 15 if(cl[p]==cr[p]){mn[p]=w;return;} 16 if(pos<=cr[p<<1])set(p<<1,pos,w); 17 else set(p<<1|1,pos,w); 18 mn[p]=min(mn[p<<1],mn[p<<1|1]); 19 } 20 int ask(int p,int l,int r){if(l>r)return 1234567; 21 if(l<=cl[p]&&cr[p]<=r)return mn[p]; 22 return min(l<=cr[p<<1]?ask(p<<1,l,r):1234567,r>=cl[p<<1|1]?ask(p<<1|1,l,r):1234567); 23 } 24 int main(){ 25 scanf("%d%d",&n,&k);build(1,1,n); 26 for(int i=1;i<=n;++i)scanf("%d",&a[i]),b[a[i]]=i;//for(int i=1;i<=n;++i)printf("%d ",b[i]);puts(""); 27 for(int i=n;i;--i){ 28 int l=ask(1,max(b[i]-k+1,1),b[i]-1),r=ask(1,b[i]+1,max(n,b[i]-k+1)); 29 if(l<1234567)link(b[l],b[i]);if(r<1234567)link(b[r],b[i]); 30 set(1,b[i],i); 31 } 32 for(int i=n;i;--i)if(!deg[i])q.push(i);int aln=n; 33 while(!q.empty()){ 34 int x=q.top();q.pop();b[aln--]=x;//printf("topu:%d\n",x); 35 for(int i=fir[x];i;i=l[i]){deg[to[i]]--;if(!deg[to[i]])q.push(to[i]);} 36 }//for(int i=1;i<=n;++i)printf("%d ",b[i]);puts(""); 37 for(int i=1;i<=n;++i)a[b[i]]=i; 38 for(int i=1;i<=n;++i)printf("%d\n",a[i]); 39 }
思路積累:
- 用拓撲排序解決限制類問題
- 問題轉化
- 字典序的結論
T3:Tree
好題。雖說做法很蠢——輸出所有邊權和。
你只要考慮並查集從大到小解鎖每一條邊就可以得到一個合法的最優排列。

1 #include<cstdio> 2 int n;long long tot,w; 3 int main(){ 4 scanf("%d",&n); 5 for(int x,y,i=1;i<n;++i)scanf("%d%d%lld",&x,&y,&w),tot+=w; 6 printf("%lld\n",tot); 7 }