题目:https://codeforces.com/contest/1092/problem/D1
https://codeforces.com/contest/1092/problem/D2
https://codeforces.com/contest/1092/problem/E
很有趣的题;
对于D1,首先发现两种砖的放法和高度的奇偶性有关(!);
而竖着放的砖不改变一列的奇偶性,也就是确定一列的奇偶性后,它的高度是可以任意的,那么我们就不用考虑实际高度的问题了;
然后发现,如果两列奇偶性相同的列相邻了,那么它们就“无敌”了,可以变成任意高度;
而两列可以合并,只能是它们相邻且奇偶性相同;
这就很像两组括号序列啊!奇数是 (),偶数是 [],那么整个序列就是 (, ), [, ] 相间的;
只要栈顶能完成一个匹配,就弹栈表示这两列“无敌”了;
所以最后要是栈里没有元素或只剩下一个元素,序列就是合法的,否则不合法;
这么简单就做完了!
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=2e5+5; int n,sta[xn],top; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } int main() { n=rd(); for(int i=1,x;i<=n;i++) { x=rd(); if(top&&(x&1)==(sta[top]&1))top--; else sta[++top]=x; } if(top<=1)puts("YES"); else puts("NO"); return 0; }
对于D2,只能放横着的砖;
那就更简单了,每次先填满最低的一段,如果其长度是奇数就不合法了,否则就和旁边的合并成一段;
这个过程可以用单调栈维护。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=2e5+5; int n,sta[xn],top,len[xn],a[xn]; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } int main() { n=rd(); for(int i=1;i<=n;i++)a[i]=rd(); for(int i=1,l,j;i<=n;i=j+1) { j=i; l=0; while(a[i]==a[j+1])j++; while(top&&a[i]>sta[top]) { if(len[top]&1){puts("NO"); return 0;} l=len[top]; top--; if(top&&sta[top]<a[i])len[top]+=l; else break; } while(top&&sta[top]==a[i])l+=len[top--]; sta[++top]=a[i]; len[top]=l+(j-i+1); //printf("sta[%d]=%d len=%d ",top,sta[top],len[top]); } int l=0; while(top>1) { l+=len[top--]; if(l&1){puts("NO"); return 0;} } puts("YES"); return 0; }
E题要最小化连通块合成的树的直径,还要输出连边方案;
结论就是找到所有连通块(小树)的直径中点,最大的直径的中点连接所有其他中点;
想想果然很有道理,因为这样其实最终的直径还是最大连通块的直径,除非有几个连通块一样都是最大;
而其他连法都可能让直径更大;
所以只需要找直径中点即可,看了看提交记录,发现可以写得很优美,就是 dfs 同时找直径端点、长度和中点;
因为如果从一端 dfs 到另一端,那么中点一定在路径上,返回的时候记录一下即可;
然后注意特别判断有两个或三个一样大的最大连通块;
直径的性质要好好利用。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define pb push_back using namespace std; int const xn=1005; int n,hd[xn],ct,to[xn<<1],nxt[xn<<1],dis[xn],mx,d; int vis[xn]; struct N{ int x,d; N(int x=0,int d=0):x(x),d(d) {} bool operator < (const N &y) const {return d<y.d;} }; vector<N>v; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;} void dfs(int x) { vis[x]=1; if(dis[x]>mx)mx=dis[x],d=x; for(int i=hd[x],u;i;i=nxt[i]) if(vis[u=to[i]]!=1)dis[u]=dis[x]+1,dfs(u); } bool dfsx(int x) { vis[x]=2; bool ret=0; if(dis[x]>mx)mx=dis[x],ret=1; for(int i=hd[x],u;i;i=nxt[i]) if(vis[u=to[i]]!=2) { dis[u]=dis[x]+1; if(dfsx(u))ret=1; } if(ret&&dis[x]==mx/2)d=x;// return ret; } int main() { n=rd(); int m=rd(); int ans=0; for(int i=1,x,y;i<=m;i++)x=rd(),y=rd(),add(x,y),add(y,x); for(int i=1;i<=n;i++) if(!vis[i]) { dis[i]=0; mx=-1; dfs(i); dis[d]=0; mx=-1; dfsx(d); ans=max(ans,mx); v.pb(N(d,mx)); } sort(v.begin(),v.end()); int siz=v.size(); if(siz>1) { ans=max(ans,(v[siz-1].d+1)/2+(v[siz-2].d+1)/2+1);// if(siz>2)ans=max(ans,(v[siz-2].d+1)/2+(v[siz-3].d+1)/2+2);// } printf("%d ",ans); int u=v[siz-1].x; for(int i=0;i<siz-1;i++) printf("%d %d ",u,v[i].x); return 0; }