BZOJ1562: [NOI2009]变换序列

BZOJ1562: [NOI2009]变换序列

Description

Input

Output

Sample Input

5
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;
}
原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9691654.html