AtCoder Regular Contest 097 题解

123 篇文章 0 订阅
40 篇文章 0 订阅

C K-th Substring


k才5,随便做都行。当然也可以SAM求第k大

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <string>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define copy(x,t) memcpy(x,t,sizeof(x))

const int N=100005;

char s[N];

int rec[N][26],size[N],len[N],fa[N];
int b[N],rk[N],last,tot;

void extend(int ch) {
	int p,q,np,nq;
	p=last; last=np=++tot;
	len[np]=len[p]+1;
	for (;p&&!rec[p][ch];p=fa[p]) rec[p][ch]=np;
	if (!p) fa[np]=1;
	else {
		q=rec[p][ch];
		if (len[q]==len[p]+1) fa[np]=q;
		else {
			nq=++tot; len[nq]=len[p]+1;
			copy(rec[nq],rec[q]);
			fa[nq]=fa[q];
			fa[np]=fa[q]=nq;
			for (;p&&rec[p][ch]==q;p=fa[p]) rec[p][ch]=nq;
		}
	}
}

void build() {
	rep(i,1,tot) b[len[i]]++;
	rep(i,1,tot) b[i]+=b[i-1];
	rep(i,1,tot) rk[b[len[i]]--]=i;
	drp(ti,tot,1) {
		int x=rk[ti]; size[x]++;
		rep(i,0,25) size[x]+=size[rec[x][i]];
	}
}

void kth(int k) {
	for (int x=1;k;) {
		rep(i,0,25) if (rec[x][i]) {
			int y=rec[x][i];
			if (size[y]<k) k-=size[y];
			else {
				putchar('a'+i);
				k--,x=y; break;
			}
		}
	}
}

int main(void) { tot=last=1;
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int k; scanf("%s%d",s+1,&k);
	int n=strlen(s+1);
	rep(i,1,n) {
		extend(s[i]-'a');
	}
	build();
	kth(k);
	putchar('\n');
	return 0;
}

D Equals


把交换看成边,那么一个连通块内的点一定可以随便换
于是我们dfs维护一下连通性,然后开个桶就可以了

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int N=400005;

std:: vector <int> v;

struct edge {int x,y,next;} e[N];

int vis[N],cnt[N],a[N];
int ls[N],edCnt,ans;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
	e[++edCnt]=(edge) {y,x,ls[y]}; ls[y]=edCnt;
}

void dfs(int x) {
	vis[x]=1; v.push_back(x);
	for (int i=ls[x];i;i=e[i].next) {
		if (vis[e[i].y]) continue;
		dfs(e[i].y);
	}
}

int main(void) {
	int n=read(),m=read();
	rep(i,1,n) a[i]=read();
	rep(i,1,m) add_edge(read(),read());
	rep(i,1,n) if (!vis[i]) {
		v.clear();
		dfs(i);
		for (int x:v) ++cnt[x];
		for (int x:v) if (cnt[a[x]]) ans++;
		for (int x:v) --cnt[x];
	}
	printf("%d\n", ans);
	return 0;
}

E Sorted and Sorted


貌似做过的。。
设f[i,j]表示前i个白球j个黑球摆好了,我们讨论第i+j个放什么颜色的球
考虑怎么算加入最后一个球之后增加的答案,很显然就是它和前面i+j-1个球组成的逆序对数量

#include <stdio.h>
#include <string.h>
#include <algorithm>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int INF=0x3f3f3f3f;
const int N=4005;

int f[N][N],a[N],w[N],b[N];
int cw[N][N],cb[N][N];

int main(void) {
	int n; scanf("%d",&n);
	rep(i,1,n*2) {
		char ch[2]; scanf("%s%d",ch,&a[i]);
		if (ch[0]=='W') w[a[i]]=i;
		else b[a[i]]=i;
	}
	rep(i,1,n) {
		rep(j,1,i-1) cw[i][0]+=(w[j]>w[i]);
		rep(j,1,n) cw[i][j]=cw[i][j-1]+(b[j]>w[i]);
	}
	rep(i,1,n) {
		rep(j,1,i-1) cb[0][i]+=(b[j]>b[i]);
		rep(j,1,n) cb[j][i]=cb[j-1][i]+(w[j]>b[i]);
	}
	rep(i,1,n) f[i][0]=f[i-1][0]+cw[i][0];
	rep(j,1,n) f[0][j]=f[0][j-1]+cb[0][j];
	rep(i,1,n) rep(j,1,n) {
		f[i][j]=std:: min(f[i-1][j]+cw[i][j],f[i][j-1]+cb[i][j]);
	}
	printf("%d\n", f[n][n]);
	return 0;
}

F Monochrome Cat


写死我了。。
很容易想到一个树形dp,设f[i],g[i],h[i]分别表示路径两端都在i黑完整个子树的答案,从i开始黑完整个子树不回到i的答案,路径的两端都在子树内且经过i的答案。

转移的时候要考虑一下i的颜色和儿子的数量

#include <stdio.h>
#include <string.h>
#include <algorithm>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fi first
#define se second

typedef std:: pair <int,int> pair;
const int INF=0x3f3f3f3f;
const int N=200005;

struct edge {int x,y,next;} e[N*2];

int f[N],g[N],h[N],size[N];
int ls[N],edCnt;
int Ah[N],r[N];

char s[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
	e[++edCnt]=(edge) {y,x,ls[y]}; ls[y]=edCnt;
}

void dfs1(int x,int fa) {
	size[x]=(s[x]=='W');
	for (int i=ls[x];i;i=e[i].next) {
		if (e[i].y==fa) continue;
		dfs1(e[i].y,x),size[x]+=size[e[i].y];
	}
}

void dfs2(int x,int fa) {
	int sum=0,son=0;
	for (int i=ls[x];i;i=e[i].next) {
		if (e[i].y==fa||!size[e[i].y]) continue;
		dfs2(e[i].y,x),++son;
		sum+=f[e[i].y];
	}
	if (!son&&s[x]=='W') return (void) (f[x]=g[x]=1);
	int wjp=son&1;
	f[x]=sum+son+1;
	if (s[x]=='W') f[x]+=wjp;
	else f[x]+=!wjp;
	for (int i=ls[x];i;i=e[i].next) {
		if (e[i].y==fa||!size[e[i].y]) continue;
		g[x]=std:: min(g[x],sum-f[e[i].y]+g[e[i].y]);
	}
	g[x]=sum+son;
	if (s[x]=='W') g[x]+=!wjp;
	else g[x]+=wjp;
	g[x]=std:: min(g[x],f[x]);
	h[x]=INF;
	if (son>1) {
		int mx1=0,mx2=0;
		for (int i=ls[x];i;i=e[i].next) {
			if (e[i].y==fa||!size[e[i].y]) continue;
			int v=f[e[i].y]-g[e[i].y];
			if (v>mx1) mx2=mx1,mx1=v;
			else if (v>mx2) mx2=v;
		}
		h[x]=sum-mx1-mx2+son-1;
		if (s[x]=='W') h[x]+=wjp,r[x]=wjp;
		else h[x]+=!wjp,r[x]=!wjp;
	}
	for (int i=ls[x];i;i=e[i].next) {
		if (e[i].y==fa||!size[e[i].y]) continue;
		int v=sum-f[e[i].y]+h[e[i].y]+son+2*!r[e[i].y];
		int t=(s[x]=='W')?(!wjp):wjp;
		if (v+t<h[x]) h[x]=v+t,r[x]=t;
	}
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int n=read();
	rep(i,2,n) add_edge(read(),read());
	scanf("%s",s+1);
	dfs1(1,0);
	dfs2(1,0);
	// solve(1,0);
	int ans=INF;
	rep(i,1,n) {
        printf("%d %d %d\n", f[i],g[i],h[i]);
	}
	rep(i,1,n) if (size[i]==size[1]) {
		ans=std:: min(ans,f[i]);
		ans=std:: min(ans,g[i]);
		ans=std:: min(ans,h[i]);
	}
	ans=(size[1]<=1)?size[1]:ans;
	printf("%d\n", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值