UOJ Logo SHYI的博客

博客

【NOI2016】优秀的拆分 题解(95分)

2016-08-02 10:21:49 By SHYI

题目大意:求一个字符串中形如AABB的子串个数。

思路:用哈希做到O(1)判断字符串是否相同,O($n^2$)预处理,ans[i]为开头位置为i的形如AA的子串个数。再用O($n^2$)枚举出AABB中的AA,加上BB(已预处理)的个数即可。时间复杂度为O($n^2$),最后一个点过不掉~~。(此方法为在下所想的朴素算法,比不得大神们的方法,代码更是烂得要死)

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int S=999983,mod=100000009;
char s[40000];
long long ans[40000],Hash[40000],mi[40000];

int gethash(int l,int r)
{
    return (Hash[r]-Hash[l-1]*mi[r-l+1]%mod+mod)%mod;
}

int main()
{
    int n;
    scanf("%d",&n);
    while (n--)
    {
        int cnt,i,j;
        long long sum=0;
        scanf("%s",s+1); cnt=strlen(s+1);
        for (i=1;i<=cnt;i++) Hash[i]=(Hash[i-1]*S+s[i]-'a'+1)%mod;
        for (mi[0]=i=1;i<=cnt;i++) mi[i]=mi[i-1]*S%mod;
        for (i=1;i<=cnt;i++) ans[i]=0;
        for (i=1;i<=cnt;i++)
            for (j=i;j+j-i+1<=cnt;j++)
                if (gethash(i,j)==gethash(j+1,j+j-i+1)) ans[i]++;
        for (i=2;i<cnt-1;i++)
            for (j=i;j>i+1>>1;j--)
                if (gethash(j,i)==gethash(j-i+j-1,j-1)) sum+=ans[i+1];
        printf("%d\n",sum);
    }
    return 0;
}

【NOI2016】区间 题解

2016-08-02 10:04:10 By SHYI

题目大意:有n个区间,当有m个区间有公共部分时,求m个区间长度的最大值与最小值之差的最小值。

思路:按区间的长度从小到大排序,可知连续的几个区间最优,则用两个指针指其头尾,线性扫描,再用线段树区间覆盖。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 1000009
#define INF 2147483647
using namespace std;

int n,m,i,j,cnt,ans=INF,sum[N<<2],lazy[N<<2],b[N<<1];
struct node{ int l,r,len; } a[N];

bool cmp(const node &x,const node &y)
{
    return x.len<y.len;
}

int find(int l,int r,int x)
{
    if (l==r) return l;
    int mid=l+r>>1;
    if (x<=b[mid]) find(l,mid,x);
    else find(mid+1,r,x);
}

void up_date(int k,int x)
{
    sum[k]+=x,lazy[k]+=x;
}

void change(int cur,int L,int R,int l,int r,int val)
{
    if (L==l && R==r) { up_date(cur,val); return; }
    int mid=L+R>>1;
    if (lazy[cur])
    {
        up_date(cur<<1,lazy[cur]);
        up_date(cur<<1|1,lazy[cur]);
        lazy[cur]=0;
    }
    if (r<=mid) change(cur<<1,L,mid,l,r,val);
    else if (l>mid) change(cur<<1|1,mid+1,R,l,r,val);
         else change(cur<<1,L,mid,l,mid,val),change(cur<<1|1,mid+1,R,mid+1,r,val);
    sum[cur]=max(sum[cur<<1],sum[cur<<1|1]);
}

void solve()
{
    for (i=1;i<=n;i++)
    {
        while (sum[1]<m)
        {
            if (j==n) return; j++;
            change(1,1,cnt,a[j].l,a[j].r,1);
        }
        ans=min(ans,a[j].len-a[i].len);
        change(1,1,cnt,a[i].l,a[i].r,-1);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].l,&a[i].r);
        b[++cnt]=a[i].l,b[++cnt]=a[i].r;
        a[i].len=a[i].r-a[i].l;
    }
    sort(a+1,a+n+1,cmp),sort(b+1,b+cnt+1);
    for (i=1;i<=n;i++) a[i].l=find(1,cnt,a[i].l),a[i].r=find(1,cnt,a[i].r);
    solve(); printf("%d\n",ans<INF?ans:-1);
    return 0;
}

bzoj3083 遥远的国度 题解

2016-08-02 09:31:15 By SHYI

题目大意:给定一棵有根树,每个点有一个权值,提供三种操作:

1.将x节点变为根节点

2.将x到y路径上的点的权值全部改为v

3.询问x的子树中点权的最小值

思路:用DFS序剖分,记录每个节点入栈出栈的时间,其之间的区间即为子树。操作2用线段树直接搞,而换根先不管,可用原来的DFS序。询问时分类讨论:记最开始的根为root,换根之后,对于当前的根rtnow和询问子树U而言,

①rtnow==U,询问整棵树

②fa[rtnow]==U,询问除了rtnow所在子树以外的整棵树

③rtnow在U的子树里,且距离大于1,询问除了rtnow的除了其祖先是U的儿子的祖先的子树以外的整棵树

④rtnow不在U的子树里,询问U的子树

代码:

#include<cstdio>
#include<iostream>
#define M 2000000
#define INF 2147483647
using namespace std;

int cnt,dfn,n,m,a[M],to[M],hson[M],next[M],head[M],id[M],size[M],last[M],pos[M],pa[M],deep[M],top[M],cov[M<<2],minv[M<<2];

void ins(int x,int y)
{
     to[++cnt]=y,next[cnt]=head[x],head[x]=cnt;
}

void dfs1(int x)
{
    size[x]=1;
    for (int i=head[x];i;i=next[i])
        if (to[i]!=pa[x])
        {
            pa[to[i]]=x,deep[to[i]]=deep[x]+1;
            dfs1(to[i]),size[x]+=size[to[i]];
            if (size[to[i]]>size[hson[x]]) hson[x]=to[i];
        }
}

void dfs2(int x,int tp)
{
    id[x]=++dfn,pos[dfn]=x,top[x]=tp;
    if (hson[x]) dfs2(hson[x],tp);
    for (int i=head[x];i;i=next[i])
        if (to[i]!=pa[x]&&to[i]!=hson[x]) dfs2(to[i],to[i]);
    last[x]=dfn;
}

void push_down(int k)
{
     if (cov[k])
     {
         cov[k<<1]=cov[k<<1|1]=minv[k<<1]=minv[k<<1|1]=cov[k];
         cov[k]=0;
     }
}

void change(int cur,int L,int R,int l,int r,int val)
{
     if (L==l && R==r) { cov[cur]=minv[cur]=val; return; }
     int mid=L+R>>1; push_down(cur);
     if (r<=mid) change(cur<<1,L,mid,l,r,val);
     else if (l>mid) change(cur<<1|1,mid+1,R,l,r,val);
          else change(cur<<1,L,mid,l,mid,val),change(cur<<1|1,mid+1,R,mid+1,r,val);
     minv[cur]=min(minv[cur<<1],minv[cur<<1|1]);
}

int ask(int cur,int L,int R,int l,int r)
{
    if (L==l && R==r) return minv[cur];
    int mid=L+R>>1; push_down(cur);
    if (r<=mid) return ask(cur<<1,L,mid,l,r);
    if (l>mid) return ask(cur<<1|1,mid+1,R,l,r);
    return min(ask(cur<<1,L,mid,l,mid),ask(cur<<1|1,mid+1,R,mid+1,r));
}

void add(int x,int y,int val)
{
     for (;top[x]!=top[y];x=pa[top[x]])
     {
         if (deep[top[x]]<deep[top[y]]) swap(x,y);
         change(1,1,n,id[top[x]],id[x],val);
     }
     if (deep[x]>deep[y]) swap(x,y);
     change(1,1,n,id[x],id[y],val);
}

int main()
{
    scanf("%d%d",&n,&m);
    int i,x,y,z,op,rt,root=0;
    for (i=1;i<n;i++) scanf("%d%d",&x,&y),ins(x,y),ins(y,x);
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d",&rt);dfs1(rt),dfs2(rt,rt);
    for (i=1;i<=n;i++) change(1,1,n,id[i],id[i],a[i]);
    while (m--)
    {
          scanf("%d",&op);
          if (op==1) scanf("%d",&root);
          if (op==2) scanf("%d%d%d",&x,&y,&z),add(x,y,z);
          if (op==3)
          {
               scanf("%d",&x);
               if (x==root) printf("%d\n",ask(1,1,n,1,n));
               else if (pa[root]==x) printf("%d\n",min(ask(1,1,n,1,id[root]-1),last[root]==n?INF:ask(1,1,n,last[root]+1,n)));
                    else if (id[root]>=id[x] && id[root]<=last[x])
                         {
                              y=root;
                              while (pa[top[y]]!=x && top[x]!=top[y]) y=pa[top[y]];
                              if (pa[top[y]]!=x) y=pos[id[x]+1];
                              else y=top[y];
                              printf("%d\n",min(ask(1,1,n,1,id[y]-1),last[y]==n?INF:ask(1,1,n,last[y]+1,n)));
                         }
                         else printf("%d\n",ask(1,1,n,id[x],last[x]));
          }
    }
    return 0;
}

[Sdoi2014]旅行 题解

2016-08-01 03:37:10 By SHYI

题目大意:给出一个n个点的数,和m次操作。每个点有颜色和权值。 每次操作分4种 1:修改一个点的颜色 2:修改一个点的权值 3:询问从x到y的路径上,和x相同颜色的点的权值和(保证x,y同颜色) 4:询问从x到y的路径上,和x相同颜色的点的权值最大值(保证x,y同颜色)

思路:树链剖分,用线段树来维护和以及最大值。

代码:

#include<cstdio>
#include<iostream>
#define M 3000009
using namespace std;

int cnt,dfn,num,n,m,w[M],c[M],to[M<<1],next[M<<1],head[M],deep[M],size[M],pa[M],id[M],top[M],root[M],sum[M<<2],mx[M<<2],lc[M<<2],rc[M<<2];

void ins(int x,int y)
{
     to[++cnt]=y,next[cnt]=head[x],head[x]=cnt;
}

void dfs1(int x)
{
     size[x]=1;
     for (int i=head[x];i;i=next[i])
         if (pa[x]!=to[i])
         {
              deep[to[i]]=deep[x]+1,pa[to[i]]=x;
              dfs1(to[i]),size[x]+=size[to[i]];
         }
}

void dfs2(int x,int chain)
{
     int i,k=0;
     id[x]=++dfn,top[x]=chain;
     for (i=head[x];i;i=next[i])
         if (deep[to[i]]>deep[x] && size[to[i]]>size[k]) k=to[i];
     if (!k) return; dfs2(k,chain);
     for (i=head[x];i;i=next[i])
         if (pa[x]!=to[i] && k!=to[i]) dfs2(to[i],to[i]);
}

void up_date(int k)
{
     sum[k]=sum[lc[k]]+sum[rc[k]];
     mx[k]=max(mx[lc[k]],mx[rc[k]]);
}

void change(int &cur,int l,int r,int x,int val)
{
     if (!cur) cur=++num;
     if (l==r) { sum[cur]=mx[cur]=val; return; }
     int mid=l+r>>1;
     if (x<=mid) change(lc[cur],l,mid,x,val);
     else change(rc[cur],mid+1,r,x,val);
     up_date(cur);
}

int ask_sum(int cur,int L,int R,int l,int r)
{
    if (!cur) return 0;
    if (L==l && R==r) return sum[cur];
    int mid=L+R>>1;
    if (r<=mid) return ask_sum(lc[cur],L,mid,l,r);
    if (l>mid) return ask_sum(rc[cur],mid+1,R,l,r);
    return ask_sum(lc[cur],L,mid,l,mid)+ask_sum(rc[cur],mid+1,R,mid+1,r);
}

int ask_max(int cur,int L,int R,int l,int r)
{
    if (!cur) return 0;
    if (L==l && R==r) return mx[cur];
    int mid=L+R>>1;
    if (r<=mid) return ask_max(lc[cur],L,mid,l,r);
    if (l>mid) return ask_max(rc[cur],mid+1,R,l,r);
    return max(ask_max(lc[cur],L,mid,l,mid),ask_max(rc[cur],mid+1,R,mid+1,r));
}

int Ask_Sum(int x,int y,int z)
{
    int ans=0;
    for (;top[x]!=top[y];x=pa[top[x]])
    {
        if (deep[top[x]]<deep[top[y]]) swap(x,y);
        ans+=ask_sum(root[z],1,n,id[top[x]],id[x]);
    }
    if (deep[x]>deep[y]) swap(x,y);
    return ans+ask_sum(root[z],1,n,id[x],id[y]);
}

int Ask_Max(int x,int y,int z)
{
    int ans=-10000000;
    for (;top[x]!=top[y];x=pa[top[x]])
    {
        if (deep[top[x]]<deep[top[y]]) swap(x,y);
        ans=max(ans,ask_max(root[z],1,n,id[top[x]],id[x]));
    }
    if (deep[x]>deep[y]) swap(x,y);
    return max(ans,ask_max(root[z],1,n,id[x],id[y]));
}

int main()
{
    scanf("%d%d",&n,&m);
    int i,x,y;
    for (i=1;i<=n;i++) scanf("%d%d",&w[i],&c[i]);
    for (i=1;i<n;i++) scanf("%d%d",&x,&y),ins(x,y),ins(y,x);
    dfs1(1),dfs2(1,1);
    for (i=1;i<=n;i++) change(root[c[i]],1,n,id[i],w[i]);
    for (i=1;i<=m;i++)
    {
        char ch[9];
        scanf("%s%d%d",ch,&x,&y);
        if (ch[0]=='C')
            if (ch[1]=='C') change(root[c[x]],1,n,id[x],0),change(root[y],1,n,id[x],w[x]),c[x]=y;
            else change(root[c[x]],1,n,id[x],y),w[x]=y;
        else if (ch[1]=='S') printf("%d\n",Ask_Sum(x,y,c[x]));
             else printf("%d\n",Ask_Max(x,y,c[x]));
    }scanf("%d",&n);
    return 0;
}

[SDOI2011]染色 题解

2016-08-01 01:43:27 By SHYI

题目大意:给定一棵有n个节点的无根树和m个操作,操作有2类:

1、将节点a到节点b路径上所有点都染成颜色c;

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段)

思路:树剖之后,维护其两端的颜色、答案和标记即可。

代码:

#include<cstdio>
#include<iostream>
#define N 100001
using namespace std;
int n,m,cnt,dfn,vis[N],head[N],to[N<<1],next[N<<1],deep[N],size[N],top[N],id[N],v[N],pa[N],lazy[N<<2],lc[N<<2],rc[N<<2],sum[N<<2];

void ins(int x,int y)
{
     to[++cnt]=y,next[cnt]=head[x],head[x]=cnt;
}

void dfs1(int x)
{
    size[x]=vis[x]=1;
    for (int i=head[x];i;i=next[i])
        if (!vis[to[i]])
        {
            deep[to[i]]=deep[x]+1;
            pa[to[i]]=x;
            dfs1(to[i]);
            size[x]+=size[to[i]];
        }
}

void dfs2(int x,int chain)
{
    int k=0,i;
    id[x]=++dfn;
    top[x]=chain;
    for (i=head[x];i;i=next[i])
        if (deep[to[i]]>deep[x] && size[to[i]]>size[k]) k=to[i];
    if (!k) return;
    dfs2(k,chain);
    for (i=head[x];i;i=next[i])
        if (deep[to[i]]>deep[x] && to[i]!=k) dfs2(to[i],to[i]);
}

void build(int l,int r,int cur)
{
    sum[cur]=1,lazy[cur]=-1;
    if(l==r)return;
    int mid=l+r>>1;
    build(l,mid,cur<<1),build(mid+1,r,cur<<1|1);
}

void update(int k)
{
    lc[k]=lc[k<<1],rc[k]=rc[k<<1|1];
    if (rc[k<<1]^lc[k<<1|1]) sum[k]=sum[k<<1]+sum[k<<1|1];
    else sum[k]=sum[k<<1]+sum[k<<1|1]-1;
}

void pushdown(int l,int r,int k)
{
    int tmp=lazy[k]; lazy[k]=-1;
    if (tmp==-1 || l==r) return;
    sum[k<<1]=sum[k<<1|1]=1;
    lazy[k<<1]=lazy[k<<1|1]=tmp;
    lc[k<<1]=rc[k<<1]=tmp;
    lc[k<<1|1]=rc[k<<1|1]=tmp;
}

void change(int L,int R,int l,int r,int cur,int val)
{
    if (l==L && r==R) { lc[cur]=rc[cur]=val; sum[cur]=1; lazy[cur]=val; return; }
    int mid=L+R>>1; pushdown(L,R,cur);
    if (r<=mid) change(L,mid,l,r,cur<<1,val);
    else if (l>mid) change(mid+1,R,l,r,cur<<1|1,val);
         else change(L,mid,l,mid,cur<<1,val),change(mid+1,R,mid+1,r,cur<<1|1,val);
    update(cur);
}


int ask(int L,int R,int l,int r,int cur)
{
    if (l==L && r==R) return sum[cur];
    int mid=L+R>>1; pushdown(L,R,cur);
    if (r<=mid) return ask(L,mid,l,r,cur<<1);
    else if (l>mid) return ask(mid+1,R,l,r,cur<<1|1);
         else
         {
              int tmp=1;
              if (rc[cur<<1]^lc[cur<<1|1]) tmp=0;
              return ask(L,mid,l,mid,cur<<1)+ask(mid+1,R,mid+1,r,cur<<1|1)-tmp;
         }
}

int getc(int l,int r,int cur,int x)
{
    if (l==r) return lc[cur];
    int mid=l+r>>1; pushdown(l,r,cur);
    if (x<=mid) return getc(l,mid,cur<<1,x);
    else return getc(mid+1,r,cur<<1|1,x);
}

int solvesum(int x,int y)
{
    int sum=0;
    for (;top[x]!=top[y];x=pa[top[x]])
    {
        if (deep[top[x]]<deep[top[y]]) swap(x,y);
        sum+=ask(1,n,id[top[x]],id[x],1);
        if (getc(1,n,1,id[top[x]])==getc(1,n,1,id[pa[top[x]]])) sum--;
    }
    if (deep[x]>deep[y]) swap(x,y);
    return sum+=ask(1,n,id[x],id[y],1);
}

void solvechange(int x,int y,int val)
{
    for (;top[x]!=top[y];x=pa[top[x]])
    {
        if (deep[top[x]]<deep[top[y]]) swap(x,y);
        change(1,n,id[top[x]],id[x],1,val);
    }
    if (deep[x]>deep[y]) swap(x,y);
    change(1,n,id[x],id[y],1,val);
}

int main()
{
    int i,a,b,c;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&v[i]);
    for(i=1;i<n;i++) scanf("%d%d",&a,&b),ins(a,b),ins(b,a);
    dfs1(1),dfs2(1,1),build(1,n,1);
    for(i=1;i<=n;i++) change(1,n,id[i],id[i],1,v[i]);
    for(i=1;i<=m;i++)
    {
        char ch[10];
        scanf("%s",ch);
        if(ch[0]=='Q')
        {
            scanf("%d%d",&a,&b);
            printf("%d\n",solvesum(a,b));
        }
        else
        {
            scanf("%d%d%d",&a,&b,&c);
            solvechange(a,b,c);
        }
    }
    return 0;
}
SHYI Avatar