[UNR #5]获奖名单

获奖名单

题解

为什么我在网上没找到其它题解呀,大概是都觉得太简单了。

首先,他要求的是回文的匹配,我们可以考虑将它拆成两段,使这两段一样。
考虑一个经典的做法,不断在上下加一个字符串,使得匹配向前延伸。
大概就是这个样子,不断地扩增下去。
A—>A_—>ABA—>ABA_
_—>AB—>AB_—>ABAC
显然,它每次都相当于填补一个之前缺的字符,再补上一个多的字符,使另外一行缺字符。

我们不妨定义点 i i i表示缺字符 i i i,多定义一个虚点 m + 1 m+1 m+1表示一个字符都不缺。
显然,每个名字都相当于一条边,连接了两个点。
m + 1 m+1 m+1走一个环走回来就相当于填满了上下的两个字符串。
那么显然,我们从点 m + 1 m+1 m+1出发走一个欧拉回路就能用上所有得到字符了。
但还有这种可能,就是有的边根本不与点 m + 1 m+1 m+1联通,但我们可以发现这种情况产生的别的连通块的边必然重复出现的,也就是说,我们对于这种边,只需要将重边放在上下两边即可。
另外一点就是我们我们放在最中间的名字还有可能是与自己对称的,它并未形成上下的字符串,这种情况这个名字必然是单出来的,我们特别处理一下即可。

时间复杂度 O ( n ∼ n log ⁡ n ) O\left(n\sim n\log n\right) O(nnlogn),可以排序去重,也可以用unordered_map什么的线性去重。
我的代码是 O ( n log ⁡ n ) O\left(n\log n\right) O(nlogn)的。

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define MAXN 500005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double Ld;
typedef pair<int,int> pii;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
const int mod=1e6+7;
const int inv2=499122177;
const int jzm=2333;
const int zero=2000;
const int n1=100;
const int lim=100000;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-9;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0)putchar('-'),print(-x);if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,m,head[MAXN],tot,len[MAXN],deg[MAXN],ord[MAXN],tota,totb,totv1,totv2,cur[MAXN];
pii ans[MAXN],ep[MAXN],op[MAXN],A[MAXN],B[MAXN],vA[MAXN],vB[MAXN];
int lena,lenb,Rt,L,R;bool vis[MAXN];
struct edge{int to,nxt,paid,rev;}e[MAXN<<2];
void addEdge(int u,int v,int w,int r){e[++tot]=(edge){v,head[u],w,r};head[u]=tot;deg[u]++;}
void dfs(int u){
	for(int i=head[u];i;head[u]=i=e[head[u]].nxt){
		int v=e[i].to,w=e[i].paid;
		if(vis[w])continue;vis[w]=1;dfs(v);
		if(Rt>m){
			if(!w){if(lena<=lenb)lena++;else lenb++;}
			else{
				if(lena<=lenb)A[++tota]=mkpr(w,e[i].rev^1),lena+=len[w];
				else B[++totb]=mkpr(w,e[i].rev^1),lenb+=len[w];
			}
		}
		else A[++tota]=mkpr(w,0);
	}
}
bool cmp(int x,int y){return ep[x]<ep[y];}
bool operator == (pii x,pii y){return x.fir==y.fir&&x.sec==y.sec;}
int main(){
	read(n);read(m);L=1,R=n;
	for(int i=1;i<=n;i++){
		int x,y;read(len[i]);
		if(len[i]==1)read(x),addEdge(m+1,x,i,0),addEdge(x,m+1,i,1);
		else read(x),read(y),addEdge(x,y,i,0),addEdge(y,x,i,1),ep[i]=mkpr(min(x,y),max(x,y)),op[i]=mkpr(x,y);
	}
	for(int i=1;i<=m;i++)if(deg[i]&1)addEdge(m+1,i,0,0),addEdge(i,m+1,0,0);
	for(int i=m+1;i>0;i--){
		Rt=i;dfs(i);lena=lenb=0;bool mid=0;
		if(i<=m){
			int siz=tota,md=-1;
			for(int j=1;j<=tota;j++)ord[j]=A[j].fir;
			sort(ord+1,ord+siz+1,cmp);tota=totb=0;
			for(int j=1;j<=siz;j++){
				if(j==siz){md=j;break;}
				if(ep[ord[j]]==ep[ord[j+1]]){
					A[++tota]=mkpr(ord[j],(ep[ord[j]]==op[ord[j]]));
					B[++totb]=mkpr(ord[j+1],(ep[ord[j+1]]==op[ord[j+1]]));
					j++;continue;
				}
				else md=j;
			}
			if(md>=0)mid=1,A[++tota]=mkpr(ord[md],0);
		}
		else{
			for(int j=1;j<=tota;j++)lena+=len[A[j].fir];
			for(int j=1;j<=totb;j++)lenb+=len[B[j].fir];
			if(lena^lenb)mid=1;lena=lenb=0;
		}
		if(mid){
			totv1=tota;for(int j=1;j<=tota;j++)vA[j]=A[j];
			totv2=totb;for(int j=1;j<=totb;j++)vB[j]=B[j];
		}
		else{
			for(int j=1;j<=tota;j++)ans[L]=A[j],L++;
			for(int j=1;j<=totb;j++)ans[R]=B[j],ans[R].sec^=1,R--;
		}
		tota=totb=0;
	}
	lena=lenb=0;
	for(int j=1;j<=totv1;j++)lena+=len[vA[j].fir];
	for(int j=1;j<=totv2;j++)lenb+=len[vB[j].fir];
	if(lena<lenb){
		for(int j=1;j<=totv1;j++)ans[L]=vA[j],L++;
		for(int j=1;j<=totv2;j++)ans[R]=vB[j],ans[R].sec^=1,R--;
	}
	else{
		for(int j=1;j<=totv2;j++)ans[L]=vB[j],L++;
		for(int j=1;j<=totv1;j++)ans[R]=vA[j],ans[R].sec^=1,R--;
	}
	for(int i=1;i<=n;i++)if(len[ans[i].fir]==1)ans[i].sec=0;
	for(int i=1;i<=n;i++)printf("%d ",ans[i].fir);puts("");
	for(int i=1;i<=n;i++)printf("%d ",ans[i].sec);puts("");
	return 0;
}
 

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值