BZOJ1562: [NOI2009]变换序列
Description
Input
Output
Sample Input
5
1 1 2 2 1
1 1 2 2 1
Sample Output
1 2 4 0 3
HINT
30%的数据中N≤50;
60%的数据中N≤500;
100%的数据中N≤10000。
题解Here!
题面不是很好懂。。。
简化一下:每个数$i$能与$(i+d_i)\%N,(i-d_i+N)\%N$匹配,问每个数匹配后某位置匹配的原来的数是什么。
很明显建完边然后二分图匹配。。。
二分图匹配丢给了匈牙利。。。
但是那个字典序最小怎么整?
我们可以将每个数的匹配中,小的放前面,大的放后面,然后倒着做一遍匈牙利。
这时我们发现现有的匹配覆盖了原有的匹配。
并且字典序更小!
所以这么做是对的。
复杂度是非常松的$O(n^2)$。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define MAXN 10010 using namespace std; int n,T,c=1; int head[MAXN],val[MAXN],f[MAXN],g[MAXN],vis[MAXN],map[MAXN][2]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } bool find(int x){ for(int k=0;k<=1;k++){ if(vis[map[x][k]]==T)continue; int v=map[x][k]; vis[v]=T; if(f[v]==-1||find(f[v])){ f[v]=x;g[x]=v; return true; } } return false; } void work(){ T=1; for(int i=n-1;i>=0;i--){ if(!find(i)){ printf("No Answer"); return; } T++; } for(int i=0;i<n-1;i++)printf("%d ",g[i]); printf("%d",g[n-1]); } void init(){ int u,v,w; n=read(); memset(f,-1,sizeof(f)); for(int i=0;i<n;i++){ w=read(); u=(i-w+n)%n;v=(i+w)%n; if(u>v)swap(u,v); map[i][0]=u;map[i][1]=v; } } int main(){ init(); work(); return 0; }