本来说好的好一场烂一场。
那样的日子结束了,连着烂了两场。。。
幸亏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
类似于《菜肴制作》,用到了它的结论:
所以现在问题变成了构造最大的下标与数值互换的数组(叫pos吧)的逆向字典序最大。
注意下面这个结论并不是恒成立的(尽管据说本题成立但是没有被证明):
现在考虑如何构造吧。
忘掉原数组,现在我们的任务就是构造pos。
现在的互换规则就是相邻两位的差至少为k。
那么因为是相邻位交换,所以如果两个数差不足k那么它们永远不能互相跨越即交换相对位置。
那么对于所有i<j,pos与pos的差的绝对值小于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:
好题。虽说做法很蠢——输出所有边权和。
你只要考虑并查集从大到小解锁每一条边就可以得到一个合法的最优排列。
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 }