总结
名次和昨天还是一样,不过状态有所提升
A.事情的相似度
分析
对于字符串建立后缀自动机
那么区间 ([l,r]) 的答案就是区间中任意两个点贡献的最大值
对于后缀树上的每一个节点开一个 (set) 维护 (endpos) 集合
将子树内的点所在的 (set) 启发式合并,并在 (lca) 处计算能够贡献答案的点对
发现一个点和它的前驱后继贡献答案一定是最优的
这样最终点对的数量是 (nlogn) 级别的
把所有的点对和询问按照右端点从小到大排序依次计算
只要拿树状数组维护后缀最大值即可
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=4e5+5;
int n,q,ans[maxn],shuyu[maxn],tp;
char s[maxn];
struct jie{
int l,r,id;
friend bool operator < (const jie& A,const jie& B){
return A.r<B.r;
}
}c[maxn];
struct Node{
int l,r,val;
Node(){}
Node(rg int aa,rg int bb,rg int cc){
l=aa,r=bb,val=cc;
}
friend bool operator < (const Node& A,const Node& B){
return A.r<B.r;
}
}sta[maxn*20];
std::set<int> se[maxn];
#define sit std::set<int>::iterator
struct SAM{
int fa[maxn],len[maxn],ch[maxn][5],lst,cnt,h[maxn],tot;
struct asd{
int to,nxt;
}b[maxn];
void ad(rg int aa,rg int bb){
b[tot].to=bb;
b[tot].nxt=h[aa];
h[aa]=tot++;
}
void insert(rg int c){
rg int p=lst;
rg int np=lst=++cnt;
len[np]=len[p]+1;
for(;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else {
rg int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else {
rg int nq=++cnt;
len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];
fa[np]=fa[q]=nq;
for(;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void build(){
lst=cnt=1;
for(rg int i=1;i<=n;i++){
insert(s[i]-'0');
se[lst].insert(i);
}
}
void dfs(rg int now){
rg sit it1,it2;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==fa[now]) continue;
dfs(u);
if(se[now].size()<se[u].size()) std::swap(se[now],se[u]);
for(rg sit it=se[u].begin();it!=se[u].end();++it){
rg int tmp=*it;
se[now].insert(tmp);
if(se[now].size()>1){
it1=it2=se[now].lower_bound(tmp);
++it2;
if(it1==se[now].begin()){
sta[++tp]=Node(tmp,*it2,len[now]);
} else if(it2==se[now].end()){
--it1;
sta[++tp]=Node(*it1,tmp,len[now]);
} else {
sta[++tp]=Node(tmp,*it2,len[now]);
--it1;
sta[++tp]=Node(*it1,tmp,len[now]);
}
}
}
for(rg sit it=se[u].begin();it!=se[u].end();++it){
se[now].insert(*it);
}
}
}
void pre(){
memset(h,-1,sizeof(h));
tot=1;
for(rg int i=2;i<=cnt;i++){
ad(i,fa[i]),ad(fa[i],i);
}
dfs(1);
std::sort(sta+1,sta+tp+1);
}
}sam;
int tr[maxn];
int lb(rg int xx){
return xx&-xx;
}
int cx(rg int wz){
rg int nans=0;
for(rg int i=wz;i<=n;i+=lb(i)) nans=std::max(nans,tr[i]);
return nans;
}
void xg(rg int wz,rg int val){
for(rg int i=wz;i>0;i-=lb(i)) tr[i]=std::max(tr[i],val);
}
int main(){
n=read(),q=read();
scanf("%s",s+1);
sam.build();
for(rg int i=1;i<=q;i++){
c[i].l=read(),c[i].r=read(),c[i].id=i;
}
std::sort(c+1,c+q+1);
sam.pre();
rg int now=1,head=1;
for(rg int i=1;i<=n;i++){
while(now<=tp && sta[now].r==i){
xg(sta[now].l,sta[now].val);
now++;
}
while(head<=q && c[head].r==i){
ans[c[head].id]=cx(c[head].l);
head++;
}
}
for(rg int i=1;i<=q;i++) printf("%d
",ans[i]);
return 0;
}
B.跳蚤王国的宰相
分析
发现满足条件的点一定是树的重心
于是我们先求出原树的重心 (C),以之为根后,每个子树的大小都不超过 (frac{n}{2})
拆 (C) 连出去的边即可,因为拆下来的子树的大小肯定不超过 (frac{n}{2})
贪心地从大到小拆
记录一个前缀和二分就行了
拆到了当前点所在的子树要特判一下
分为两种情况,一种是强制拆,一种是不拆
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5;
int h[maxn],tot=1,n;
struct asd{
int to,nxt;
}b[maxn<<1];
void ad(rg int aa,rg int bb){
b[tot].to=bb;
b[tot].nxt=h[aa];
h[aa]=tot++;
}
int siz[maxn],maxsiz[maxn],rt;
void getroot(rg int now,rg int lat){
siz[now]=1;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat) continue;
getroot(u,now);
siz[now]+=siz[u];
maxsiz[now]=std::max(maxsiz[now],siz[u]);
}
maxsiz[now]=std::max(maxsiz[now],n-siz[now]);
if(maxsiz[now]<maxsiz[rt]) rt=now;
}
int shuyu[maxn],sta[maxn],tp,sum[maxn],wz[maxn];
void dfs(rg int now,rg int lat,rg int col){
shuyu[now]=col;
siz[now]=1;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat) continue;
dfs(u,now,col);
siz[now]+=siz[u];
}
}
int main(){
memset(h,-1,sizeof(h));
n=read();
rg int aa,bb;
for(rg int i=1;i<n;i++){
aa=read(),bb=read();
ad(aa,bb),ad(bb,aa);
}
maxsiz[0]=0x3f3f3f3f;
getroot(1,0);
for(rg int i=h[rt];i!=-1;i=b[i].nxt){
dfs(b[i].to,rt,b[i].to);
sta[++tp]=siz[b[i].to];
}
std::sort(sta+1,sta+tp+1);
std::reverse(sta+1,sta+tp+1);
for(rg int i=1;i<=tp;i++) wz[sta[i]]=wz[sta[i]]?wz[sta[i]]:i;
for(rg int i=1;i<=tp;i++) sum[i]=sum[i-1]+sta[i];
for(rg int i=1;i<=n;i++){
if(rt==i) printf("0
");
else {
rg int tmp=siz[shuyu[i]];
rg int nans=std::lower_bound(sum+1,sum+tp+1,n-n/2-siz[i])-sum;
if(nans>=wz[tmp]){
nans=std::min(std::lower_bound(sum+1,sum+tp+1,n-n/2+tmp-siz[i])-sum-1,std::lower_bound(sum+1,sum+tp+1,n-n/2)-sum);
}
printf("%d
",nans);
}
}
return 0;
}
C.蛐蛐国的修墙方案
分析
如果把 (i) 和 (p[i]) 连边,那么最终会形成若干个环
处理长度为 (2) 的环,每一个环有两种选择
枚举每一个环是哪一种选择,直接搜索就行
复杂度为 (n2^{frac{n}{4}})
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<vector>
#include<cstdlib>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=405;
int n,a[maxn],mp[maxn][maxn],vis[maxn],tim,sta[maxn],tp,mat[maxn];
char s[maxn];
std::vector<int> g[maxn];
void predfs(rg int now){
vis[now]=tim;
g[tim].push_back(now);
for(rg int i=1;i<=n;i++){
if(mp[now][i] && !vis[i]) predfs(i);
}
}
bool jud(){
rg int sum=0;
for(rg int i=1;i<=n;i++){
sum+=mat[i];
if(sum<0) return 0;
}
if(sum!=0) return 0;
return 1;
}
void init(rg int aa,rg int bb){
mat[std::min(aa,bb)]=1;
mat[std::max(aa,bb)]=-1;
}
void init2(rg int aa,rg int bb){
if(a[aa]==bb){
mat[aa]=1,mat[bb]=-1;
} else {
mat[aa]=-1,mat[bb]=1;
}
}
void dfs(rg int now){
if(now>tp){
if(jud()){
for(rg int i=1;i<=n;i++){
if(mat[i]<0) s[i]=')';
else s[i]='(';
}
printf("%s
",s+1);
std::exit(0);
}
return;
}
rg int tmp=sta[now],tmp2=g[tmp].size();
for(rg int i=0;i<tmp2;i+=2){
init2(g[tmp][i],g[tmp][i+1]);
}
dfs(now+1);
for(rg int i=0;i<tmp2;i++) mat[g[tmp][i]]=0;
for(rg int i=1;i<tmp2-1;i+=2){
init2(g[tmp][i],g[tmp][i+1]);
}
init2(g[tmp][0],g[tmp][tmp2-1]);
dfs(now+1);
for(rg int i=0;i<tmp2;i++) mat[g[tmp][i]]=0;
}
int main(){
n=read();
for(rg int i=1;i<=n;i++){
a[i]=read();
mp[i][a[i]]=mp[a[i]][i]=1;
}
for(rg int i=1;i<=n;i++){
if(!vis[i]){
tim++;
predfs(i);
}
}
for(rg int i=1;i<=tim;i++){
if(g[i].size()==2){
init(g[i][0],g[i][1]);
} else {
sta[++tp]=i;
}
}
dfs(1);
return 0;
}