Educational Codeforces Round 53

44 篇文章 0 订阅
19 篇文章 0 订阅

Educational Codeforces Round 53

F. Choosing Two Paths(CF 1073F)

题目描述
http://codeforces.com/contest/1073/problem/F

题解
其实就是类似树上最长链的做法,从根开始往下搜,如果一个点有多于一个孩子那它就可能作为起点。
统计出每个点的两个最深的孩子作为两个起点,然后在所有合法点钟找出符合题意的最长链就好了。

代码

#include<bits/stdc++.h>
#define N 200010
using namespace std;
int n,A,B,C,D,pos,dep[N],f[N],g[N];
int k,la[N],ff[N*2];
struct info{
  int x,y;
  bool operator<(const info &p)const{
    return x<p.x||(x==p.x&&y<p.y);
  }
}res;
struct node{int a,b;}e[N*2];
void add(int a,int b)
{
  e[++k]=(node){a,b};ff[k]=la[a];la[a]=k;
  e[++k]=(node){b,a};ff[k]=la[b];la[b]=k;
}

void dfs(int x,int fa)
{
  int cnt=0;f[x]=g[x]=0;
  for(int a=la[x];a;a=ff[a])
  {
    if(e[a].b==fa)continue;cnt++;
	dep[e[a].b]=dep[x]+1;dfs(e[a].b,x);
	if(dep[f[e[a].b]]>dep[f[x]])g[x]=f[x],f[x]=f[e[a].b];
	else if(dep[f[e[a].b]]>dep[g[x]])g[x]=f[e[a].b];
  }
  if(!cnt)f[x]=x;
  if(cnt>=2)
  {
  	info tmp=(info){dep[x],dep[f[x]]+dep[g[x]]-dep[x]*2};
    if(res<tmp)res=tmp,pos=x;
  }
}

int main()
{
  int a,b;
  scanf("%d",&n);
  for(int i=1;i<n;i++)scanf("%d%d",&a,&b),add(a,b);
  dep[1]=0;res=(info){0,0};
  dfs(1,0);A=f[pos];C=g[pos];
  dep[pos]=0;res=(info){0,0};
  dfs(pos,0);B=f[pos];D=g[pos];
  printf("%d %d\n%d %d\n",A,B,C,D);
  return 0;
} 

G. Yet Another LCP Problem(CF 1073G)

题目描述
http://codeforces.com/contest/1073/problem/G

题解
lcp就是后缀数组中height的区间最小值。所以按照这个思路做就好了。
把a数组和b数组所有数字按照后缀数组的rank排序,从左往右扫一遍,拿个单调栈维护一下最小的height和它覆盖的区间,后再从右往左扫一遍做一样的事情,就好了。
代码能力太差了,这东西写了好久。。。

代码

#include<bits/stdc++.h>
#define N 200010
#define ll long long
using namespace std;
int n,m,Q,a[N],b[N],q[N],p[N];char s[N];ll ans;
int cnt[N],x[N],y[N],t[N],sa[N],g[N][20],rk[N],height[N];
struct info{
  int x,tp,k;
  bool operator<(const info &p)const{
    if(k)return rk[x]<rk[p.x]||(x==p.x&&tp>p.tp);
    return rk[x]>rk[p.x]||(x==p.x&&tp<p.tp);
  }
}h[N*2];
bool cmp(int *g,int a,int b,int l)
{return g[a]==g[b]&&g[a+l]==g[b+l];}

void get_sa()
{
  m=128;
  for(int i=1;i<=n;i++)cnt[x[i]=s[i]]++;
  for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
  for(int i=n;i;i--)sa[cnt[x[i]]--]=i;
  for(int j=1,tot=0;tot<=n;m=tot,j<<=1)
  {
    tot=0;for(int i=n-j+1;i<=n;i++)y[++tot]=i;
    for(int i=1;i<=n;i++)if(sa[i]>j)y[++tot]=sa[i]-j;
    for(int i=1;i<=n;i++)t[i]=x[y[i]];
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=n;i++)cnt[t[i]]++;
    for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
    for(int i=n;i;i--)sa[cnt[t[i]]--]=y[i];
    tot=2;swap(x,y);x[sa[1]]=1;
    for(int i=2;i<=n;i++)
      x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?tot-1:tot++;
  }
}

void get_height()
{
  for(int i=1;i<=n;i++)rk[sa[i]]=i;
  for(int i=1,j,k=0;i<=n;height[rk[i++]]=k)
    for(k=k?k-1:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
}

void get_rmq()
{
  memset(g,127,sizeof(g));
  for(int i=1;i<=n;i++)g[i][0]=height[i];
  for(int j=1;(1<<j)<=n;j++)
    for(int i=1;i<=n-(1<<j)+1;i++)
      g[i][j]=min(g[i][j-1],g[i+(1<<j-1)][j-1]);
}

int lcp(int x,int y)
{
  if(x==y)return n-x+1;
  x=rk[x];y=rk[y];
  if(x>y)swap(x,y);x++;
  int p=log2(y-x+1);
  return min(g[x][p],g[y-(1<<p)+1][p]);
}

ll solve(int l1,int l2,int tp)
{
  ll sum=0,ans=0;int tot=0,top=0;
  for(int i=1;i<=l1;i++)h[++tot]=(info){a[i],0,tp};
  for(int i=1;i<=l2;i++)h[++tot]=(info){b[i],1,tp};
  sort(h+1,h+tot+1);
  for(int i=2,j=h[1].tp;i<=tot;i++)
  {
    int tmp=lcp(h[i-1].x,h[i].x);
    while(top&&tmp<=q[top])sum-=(p[top]-p[top-1])*q[top],top--;
	sum+=tmp*(j-p[top]);q[++top]=tmp;p[top]=j;
    if(!h[i].tp)ans+=sum;j+=h[i].tp;
  }
  return ans;
}

int main()
{
  int l1,l2;
  scanf("%d%d %s",&n,&Q,s+1);
  get_sa();get_height();get_rmq();
  while(Q--)
  {
    scanf("%d%d",&l1,&l2);ans=0;
    for(int i=1;i<=l1;i++)scanf("%d",&a[i]);
    for(int i=1;i<=l2;i++)scanf("%d",&b[i]);
    b[0]=1;b[l2+1]=n;
    ans+=solve(l1,l2,1);reverse(a,a+l1+2);
	reverse(b,b+l2+2);ans+=solve(l1,l2,0);
	printf("%I64d\n",ans);
  }
  return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值