Codeforces Round #778 (Div. 1 + Div. 2)

F. Minimal String Xoration

题目描述

点此看题

解法

\(f(s,d)\)\(t_i=s_{i\oplus d}\) 的字符串 \(t\),可以将问题转化成:把 \(f(s,0),f(s,1)...f(s,2^n-1)\) 按照字典序从小到大排序,那么字典序最小的就是答案。

那么可以考虑类似后缀数组一样倍增,假设现在我们知道在 \(2^k\) 的前缀意义下,\(f(s,0\sim2^{n}-1)\) 的大小关系,我们考虑快速计算在 \(2^{k+1}\) 的前缀意义下 \(f(s,0\sim 2^{n}-1)\) 的大小关系。

\(rk[i]\) 表示 \(f(s,i)\) 的字典序排名,那么 \(f(s,i)\) 的特征可以用 \((rk[i],rk[i\oplus 2^k])\) 来表示,我们把这个二元组排序,然后生成新一轮的 \(rk\) 即可(思路本质上就是后缀数组的思路),时间复杂度 \(O(n^22^n)\)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 1<<18;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,b[26],rk[M],sa[M],nw[M];char s[M];
signed main()
{
	n=1<<read();scanf("%s",s);
	for(int i=0;i<n;i++) b[s[i]-'a']++;
	for(int i=1;i<26;i++) b[i]+=b[i-1];
	for(int i=0;i<n;i++) rk[i]=b[s[i]-'a'];
	for(int w=1;w<n;w<<=1)
	{
		for(int i=0;i<n;i++) sa[i]=i;
		sort(sa,sa+n,[&](int i,int j)
		{return rk[i]==rk[j]?rk[i^w]<rk[j^w]:rk[i]<rk[j];});
		nw[sa[0]]=1;int num=1;
		for(int i=1;i<n;i++)
			nw[sa[i]]=(rk[sa[i]]==rk[sa[i-1]]
			&& rk[sa[i]^w]==rk[sa[i-1]^w])?num:++num;
		memcpy(rk,nw,sizeof rk);
	}
	for(int i=0;i<n;i++)
		printf("%c",s[i^sa[0]]);
	puts("");
}

G. Snowy Mountain

题目描述

点此看题

解法

比较离谱的一点是,我照着标程抄,结果把代码长度、空间、时间都变小了

考虑如果只能走下坡路的话,那么点 \(u\) 的最远滑行距离就是 \(h_u\),现在把平滑考虑进来,我们称周围存在 \(h_u=h_v\) 的点 \(u\) 为平滑点。如果我们在平滑点 \(x\) 滑行,那么滑行距离就是 \(2\cdot h_u-h_x\)(因为会损耗 \(h_x\) 的动能),所以问题转化成,我们需要对每个点 \(u\) 找到可以到达并且 \(h_x\) 最小的滑行点 \(x\)

一个关键的 \(\tt observation\) 是:\(h_x\) 不同的滑行点 \(x\) 至多只有 \(O(\sqrt n)\) 种,这是因为构造出一个滑行点 \(x\) 至少需要多划分 \(h_x\) 个点(要把路径复刻一遍),那么我们就有 \(\sum h_x\leq n\)

所以对于每种 \(h_x\) 单独处理,可以按照 \(h_u\) 的大小把所有点 \(u\) 分层,那么同层移动增加 \(1\) 的势能,向更低层移动减少 \(1\) 的势能。只能保留大于等于 \(0\) 的势能(这样才能保证可达性),如果最后势能 \(=0\) 那么就说明可以到这个平滑点。设 \(c(u,i)\) 表示从 \(u\)\(h_x=i\)\(x\) 的最小势能,按照层从小到大转移即可,时间复杂度 \(O(n\sqrt n)\)

#include <cstdio>
#include <vector>
#include <iostream>
#include <queue>
using namespace std;
const int M = 200005;
const int inf = 0x3f3f3f3f;
#define pb push_back
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,d[M],a[M],b[M],vis[M];
vector<int> g[M],f[M],c[M];queue<int> q;
void upd(int &x,int y) {x=min(x,y);}
void dfs1(int u,int fa)
{
	vis[u]=1;
	for(int v:g[u]) if((v^fa) && d[u]==d[v])
	{
		dfs1(v,u);c[u][b[d[u]]]=1;
		for(int i=1;i<=m;i++)
			upd(c[u][i],c[v][i]+1);
	}
}
void dfs2(int u,int fa)
{
	for(int v:g[u]) if((v^fa) && d[u]==d[v])
	{
		c[v][b[d[v]]]=1;
		for(int i=1;i<=m;i++)
			upd(c[v][i],c[u][i]+1);
		dfs2(v,u);
	}
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
		read()?q.push(i),0:d[i]=inf;
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		g[u].pb(v);g[v].pb(u);
	}
	//bfs
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int v:g[u])
		{
			if(d[v]>d[u]+1)
				d[v]=d[u]+1,q.push(v);
		}
	}
	//flippable
	for(int u=1;u<=n;u++) for(int v:g[u])
		if(d[u]==d[v] && !b[d[u]])
			a[++m]=d[u],b[d[u]]=m;
	for(int i=1;i<=n;i++)
		c[i].resize(m+1),f[d[i]].pb(i);
	//layer
	for(int w=0;w<=n;w++)
	{
		for(auto u:f[w])
		{
			for(int i=1;i<=m;i++) c[u][i]=inf;
			for(int v:g[u]) if(d[v]==d[u]-1)
				for(int i=1;i<=m;i++)
					upd(c[u][i],max(0,c[v][i]-1));
		}
		for(auto u:f[w]) if(!vis[u])
			dfs1(u,0),dfs2(u,0);
	}
	for(int u=1;u<=n;u++)
	{
		int ans=d[u];
		for(int i=1;i<=m;i++) if(!c[u][i])
			ans=max(ans,2*d[u]-a[i]);
		printf("%d ",ans);
	}
	puts("");
}
posted @ 2022-03-21 21:00  C202044zxy  阅读(213)  评论(0编辑  收藏  举报