A:显然最优方案是对所形成的置换的每个循环排个序。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<vector> using namespace std; #define ll long long #define N 100010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,a[N],c[N]; vector<int> b; bool flag[N]; signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(); for (int i=1;i<=n;i++) c[i]=a[i]=read(); sort(c+1,c+n+1); for (int i=1;i<=n;i++) a[i]=lower_bound(c+1,c+n+1,a[i])-c; int ans=0; for (int i=1;i<=n;i++) if (!flag[i]) { flag[i]=1;int x=i; while (!flag[a[x]]) x=a[x],flag[x]=1; ans++; } cout<<ans<<endl; memset(flag,0,sizeof(flag)); for (int i=1;i<=n;i++) if (!flag[i]) { b.clear(); int cnt=1;flag[i]=1;int x=i;b.push_back(i); while (!flag[a[x]]) x=a[x],flag[x]=1,cnt++,b.push_back(x); printf("%d ",cnt); for (int j=0;j<cnt;j++) printf("%d ",b[j]);printf(" "); } return 0; //NOTICE LONG LONG!!!!! }
B:随机问1000个位置,然后找到x在其中哪个区间内暴力询问即可。注意最好不要rand。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<ctime> #include<vector> #include<chrono> #include<random> using namespace std; #define ll long long #define N 50010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,start,x,val[N],nxt[N]; bool flag[N]; struct data{int p,val,nxt; bool operator <(const data&a) const { return val<a.val; } }a[N]; signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(),start=read(),x=read(); mt19937 rnd(time(0)); cout<<"?"<<' '<<start<<endl; flag[start]=1;cin>>val[start]>>nxt[start]; if (val[start]>=x) {cout<<"!"<<' '<<val[start]<<endl;return 0;} for (int i=1;i<=999;i++) { int x=rnd()%n+1; cout<<"?"<<' '<<x<<endl; flag[x]=1;cin>>val[x]>>nxt[x]; } int t=0; for (int i=1;i<=n;i++) if (flag[i]) a[++t].p=i,a[t].nxt=nxt[i],a[t].val=val[i]; sort(a+1,a+t+1); a[t+1].val=1010000000; for (int i=1;i<=t;i++) if (a[i].val<=x&&a[i+1].val>x) { int u=a[i].nxt,v=a[i].val; if (v>=x) {cout<<"!"<<' '<<v<<endl;return 0;} while (u!=-1&&v<x) { cout<<"?"<<' '<<u<<endl; cin>>v>>u; } if (v>=x) { cout<<"!"<<' '<<v<<endl;return 0; } else {cout<<"!"<<' '<<-1<<endl;return 0;} } return 0; //NOTICE LONG LONG!!!!! }
C:容易发现重心的各棵子树的点集是不能改变的。然后一堆人比如我就扔个点分上去肯定就假了。事实上可以通过这种操作将任意一棵子树展开成链,链可以再转化成菊花,分别消耗低于n次。第一步逐个考虑当前点的每个子树(都已经形成链),将当前点与父亲切断,并将该子树所形成的链的底端接到父亲。第二步由链底端自下往上切掉,并将原本的菊花中心接上即可。注意判一下有两个重心的情况。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<set> #include<cassert> using namespace std; #define ll long long #define N 400010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,p[N],size[N],ansx[N],ansy[N],ansz[N],fa[N],top[N],pre[N],u,root,root2,t; set<int> e[N]; struct data{int to,nxt; }edge[N<<1]; void addedge(int x,int y){t++;e[x].insert(y);edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} void make(int k,int from) { size[k]=1; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from) { make(edge[i].to,k); size[k]+=size[edge[i].to]; } } int findroot(int k,int s,int from) { int mx=0; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from&&size[edge[i].to]>size[mx]) mx=edge[i].to; if ((size[mx]<<1)>s) return findroot(mx,s,k); else return k; } void solve(int k,int from) { fa[k]=from; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from&&edge[i].to!=root&&edge[i].to!=root2) solve(edge[i].to,k); for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from&&edge[i].to!=root&&edge[i].to!=root2) { if (!top[k]) top[k]=edge[i].to; ansx[++u]=fa[k]; ansy[u]=k; ansz[u]=edge[i].to; e[ansx[u]].erase(ansy[u]),e[ansx[u]].insert(ansz[u]); e[ansy[u]].erase(ansx[u]),e[ansz[u]].insert(ansx[u]); fa[k]=top[edge[i].to]; } if (!top[k]) top[k]=k; }//����kΪ�����������һ���� int get(int x) { while (1) { auto it=e[x].begin(); while (it!=e[x].end()&&(*it)==pre[x]) it++; if (it==e[x].end()) return x; else pre[*it]=x,x=*it; } } signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(); for (int i=1;i<n;i++) { int x=read(),y=read(); addedge(x,y),addedge(y,x); } make(1,1); root=findroot(1,n,1); if (n%2==0) { for (int i=1;i<=n;i++) if (size[i]==n/2) {root2=i;break;} } if (root2) { for (int i=p[root2];i;i=edge[i].nxt) if (edge[i].to!=root) { solve(edge[i].to,root2); pre[top[edge[i].to]]=root2; int x=get(top[edge[i].to]),k=x; while (k!=root2) { ansx[++u]=pre[k]; ansy[u]=k; ansz[u]=x; k=pre[k]; } } } for (int i=p[root];i;i=edge[i].nxt) if (edge[i].to!=root2) { solve(edge[i].to,root); pre[top[edge[i].to]]=root; int x=get(top[edge[i].to]),k=x; while (k!=root) { ansx[++u]=pre[k]; ansy[u]=k; ansz[u]=x; k=pre[k]; } } printf("%d ",u); for (int i=1;i<=u;i++) printf("%d %d %d ",ansx[i],ansy[i],ansz[i]); return 0; //NOTICE LONG LONG!!!!! }
D:注意到dij的堆如果可以用桶替代就能做到线性最短路,这要求值域较小,而题面保证了总边权变化量不超过1e6。考虑怎么仅对变化量做最短路。一开始跑完dij后,将每条边(u,v)的边权重赋值为disu+w(u,v)-disv建成新图,每次修改就在新图中进行修改并跑桶优化dij,再将得到的最短路值累加,并继续对新图进行重赋值即可。好像每次边权只+1并没有什么卵用?
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> #include<vector> using namespace std; #define ll long long #define N 100010 #define inf 10000000000000000ll char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,Q,p[N],t; ll d[N],d2[N]; bool flag[N]; vector<int> a[N]; struct data{int to,nxt;ll len; }edge[N]; void addedge(int x,int y,int z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;} struct data2 { int x;ll d; bool operator <( const data2&a) const { return d>a.d; } }; priority_queue<data2> q; void dijkstra() { memset(d,42,sizeof(d));d[1]=0; q.push((data2){1,0}); for (;;) { while (!q.empty()&&flag[q.top().x]) q.pop(); if (q.empty()) break; data2 x=q.top();q.pop(); flag[x.x]=1; for (int i=p[x.x];i;i=edge[i].nxt) if (x.d+edge[i].len<d[edge[i].to]) { d[edge[i].to]=x.d+edge[i].len; q.push((data2){edge[i].to,d[edge[i].to]}); } } for (int i=1;i<=n;i++) for (int j=p[i];j;j=edge[j].nxt) edge[j].len=d[i]+edge[j].len-d[edge[j].to]; } void update(int c) { memset(d2,42,sizeof(d2)); memset(flag,0,sizeof(flag)); for (int i=0;i<=c;i++) a[i].clear(); a[0].push_back(1);d2[1]=0; for (int i=0;i<=c;i++) { for (int k=0;k<a[i].size();k++) { int x=a[i][k]; if (!flag[x]) { flag[x]=1; for (int j=p[x];j;j=edge[j].nxt) if (d2[x]+edge[j].len<d2[edge[j].to]&&d2[x]+edge[j].len<=c) { d2[edge[j].to]=d2[x]+edge[j].len; a[d2[edge[j].to]].push_back(edge[j].to); } } } } for (int i=1;i<=n;i++) for (int j=p[i];j;j=edge[j].nxt) edge[j].len=d2[i]+edge[j].len-d2[edge[j].to]; for (int i=1;i<=n;i++) if (d2[i]<inf) d[i]+=d2[i]; } signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(),m=read(),Q=read(); for (int i=1;i<=m;i++) { int x=read(),y=read(),z=read(); addedge(x,y,z); } dijkstra(); for (int i=1;i<=Q;i++) { int op=read(); if (op==1) { int x=read(); if (d[x]>inf) printf("-1 "); else printf("%I64d ",d[x]); } else { int c=read(); for (int j=1;j<=c;j++) edge[read()].len++; update(c); } } return 0; //NOTICE LONG LONG!!!!! }