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("");
}