1
21
2016
0

【bzoj3531】【Sdoi2014】旅行

3531: [Sdoi2014]旅行

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 837  Solved: 426
[Submit][Status][Discuss]

Description

 S国有N个城市,编号从1到N。城市间用N-1条双向道路连接,满足
从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。为了方便,我们用不同的正整数代表各种宗教,  S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。
    在S国的历史上常会发生以下几种事件:
”CC x c”:城市x的居民全体改信了c教;
”CW x w”:城市x的评级调整为w;
”QS x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级总和;
”QM x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过
的城市的评级最大值。
    由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。    为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

Input

    输入的第一行包含整数N,Q依次表示城市数和事件数。
    接下来N行,第i+l行两个整数Wi,Ci依次表示记录开始之前,城市i的
评级和信仰。
    接下来N-1行每行两个整数x,y表示一条双向道路。
    接下来Q行,每行一个操作,格式如上所述。

Output

    对每个QS和QM事件,输出一行,表示旅行者记下的数字。

Sample Input

5 6
3 1
2 3
1 2
3 3
5 1
1 2
1 3
3 4
3 5
QS 1 5
CC 3 1
QS 1 5
CW 3 3
QS 1 5
QM 2 4

Sample Output

8
9
11
3

HINT

 

N,Q < =10^5    , C < =10^5


 数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时

刻,城市的评级总是不大于10^4的正整数,且宗教值不大于C。

 

Source

在普通的树链剖分基础上加了一个宗教的限制,但我们发现宗教个数在105范围内,我们可以对每个宗教开一个线段树,然后只要访问到的点开点就可以了,这样就不会超内存了。挺水的

代码:

 

#include<cstdio>
#include<algorithm>
using namespace std;
struct node{int l,r,sum,max;}tr[10000010];
int n,q,edgenum,u,v,tot,cnt,x,y,z,ans;
int f[200010],d[200010],num[200010],son[200010],top[200010],tid[200010],pre[200010],root[200010];
int vet[200010],next[200010],head[200010],w[200010],c[200010];
char opt[20];
void add(int u,int v)
{
    vet[++edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
}
void dfs(int u,int fa,int dep)
{
    f[u]=fa;
    d[u]=dep;
    num[u]=1;
    int maxnum=0;
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
        if (v!=fa)
        {
            dfs(v,u,dep+1);
            num[u]+=num[v];
            if (num[v]>maxnum){maxnum=num[v];son[u]=v;}
        }
    }
}
void dfs(int u,int number)
{
    top[u]=number;
    tid[u]=++tot;
    pre[tot]=u;
    if (!son[u]) return;
    dfs(son[u],number);
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
        if (v!=f[u]&&v!=son[u]) dfs(v,v);
    }
}
int lca(int u,int v)
{
    for (;top[u]!=top[v];u=f[top[u]])if (d[top[u]]<d[top[v]]) swap(u,v);
    if (d[u]<d[v]) return u;return v;
}
void change(int l,int r,int &p,int x,int val)
{
    if (!p) p=++cnt;
    if (l==r){tr[p].max=tr[p].sum=val;return;}
    int mid=(l+r)>>1;
    if (x<=mid) change(l,mid,tr[p].l,x,val);else change(mid+1,r,tr[p].r,x,val);
    tr[p].max=max(tr[tr[p].l].max,tr[tr[p].r].max);
    tr[p].sum=tr[tr[p].l].sum+tr[tr[p].r].sum;
}
int askmax(int l,int r,int p,int x,int y)
{
    if (!p) return 0;
    if (l==x&&y==r) return tr[p].max;
    int mid=(l+r)>>1;
    if (y<=mid) return askmax(l,mid,tr[p].l,x,y);else
    if (x>mid) return askmax(mid+1,r,tr[p].r,x,y);else
    return max(askmax(l,mid,tr[p].l,x,mid),askmax(mid+1,r,tr[p].r,mid+1,y));
}
int asksum(int l,int r,int p,int x,int y)
{
    if (!p) return 0;
    if (l==x&&y==r) return tr[p].sum;
    int mid=(l+r)>>1;
    if (y<=mid) return asksum(l,mid,tr[p].l,x,y);else
    if (x>mid) return asksum(mid+1,r,tr[p].r,x,y);else
    return asksum(l,mid,tr[p].l,x,mid)+asksum(mid+1,r,tr[p].r,mid+1,y);
}
int getsum(int u,int v,int c)
{
    int ans=0;
    while (top[u]!=top[v])
    {
        ans+=asksum(1,n,root[c],tid[top[u]],tid[u]);
        u=f[top[u]];
    }
    ans+=asksum(1,n,root[c],tid[v],tid[u]);
    return ans;
}
int getmax(int u,int v,int c)
{
    int ans=0;
    while (top[u]!=top[v])
    {
        ans=max(ans,askmax(1,n,root[c],tid[top[u]],tid[u]));
        u=f[top[u]];
    }
    ans=max(ans,askmax(1,n,root[c],tid[v],tid[u]));
    return ans;
}
int main()
{
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++) scanf("%d%d",&w[i],&c[i]);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,0,1);
    dfs(1,1);
    for (int i=1;i<=n;i++) change(1,n,root[c[i]],tid[i],w[i]);
    while (q--)
    {
        scanf("%s",opt);
        scanf("%d%d",&x,&y);
        if (opt[0]=='C')
        {
            if (opt[1]=='C')
            {
                change(1,n,root[c[x]],tid[x],0);
                c[x]=y;
                change(1,n,root[c[x]],tid[x],w[x]);
            }else {change(1,n,root[c[x]],tid[x],y);w[x]=y;}
        }else
        {
            z=lca(x,y);
            if (opt[1]=='S')
            {
                ans=getsum(x,z,c[x])+getsum(y,z,c[x]);
                if (c[x]==c[z]) ans-=w[z];
                printf("%d\n",ans);
            }else printf("%d\n",max(getmax(x,z,c[x]),getmax(y,z,c[x])));
        }
    }
}
Category: 树链剖分 | Tags:
12
27
2015
0

【bzoj2243】【SDOI2011】染色

2243: [SDOI2011]染色

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 4270  Solved: 1608
[Submit][Status][Discuss]

Description

 

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

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

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“1122213段组成:“11、“222和“1

请你写一个程序依次完成这m个操作。

 

Input

第一行包含2个整数nm,分别表示节点数和操作数;

第二行包含n个正整数表示n个节点的初始颜色

下面 行每行包含两个整数xy,表示xy之间有一条无向边。

下面 行每行描述一个操作:

“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括ab)都染成颜色c

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括ab)路径上的颜色段数量。

 

Output

对于每个询问操作,输出一行答案。

 

Sample Input

6 5
 
2 2 1 2 1 1
 
1 2
 
1 3
 
2 4
 
2 5
 
2 6
 
Q 3 5
 
C 2 1 1
 
Q 3 5
 
C 5 1 2
 
Q 3 5
 

Sample Output

3
 
1
 
2

HINT

N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。

Source

树链剖分,注意一下询问的细节就可以了。

代码:

 

#include<cstdio>
#include<algorithm>
#define maxn 1000000
using namespace std;
int n,m,u,v,tot,opt,a,b,c,edgenum;
int f[maxn],d[maxn],num[maxn],son[maxn],tid[maxn],pre[maxn],top[maxn],col[maxn],vet[maxn],next[maxn],head[maxn];
char s[5];
struct node{int l,r,sum,tag;}tr[maxn];
void add(int u,int v)
{
    edgenum++;
    vet[edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
}
void dfs(int u,int dep,int fa)
{
    f[u]=fa;
    d[u]=dep;
    num[u]=1;
    int maxnum=0;
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
        if (v!=fa)
        {
            dfs(v,dep+1,u);
            num[u]+=num[v];
            if (num[v]>maxnum){maxnum=num[v];son[u]=v;}
        }
    }
}
void dfs(int u,int number)
{
    top[u]=number;
    tid[u]=++tot;
    pre[tot]=u;
    if (!son[u]) return;
    dfs(son[u],number);
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
         
        if (v!=f[u]&&v!=son[u]) dfs(v,v);
    }
}
void build(int l,int r,int p)
{
    if (l==r){tr[p].l=tr[p].r=col[pre[l]];tr[p].sum=1;tr[p].tag=-1;return;}
    int mid=(l+r)>>1;
    build(l,mid,p*2);build(mid+1,r,p*2+1);
    tr[p].l=tr[p*2].l;
    tr[p].r=tr[p*2+1].r;
    tr[p].tag=-1;
    tr[p].sum=tr[p*2].sum+tr[p*2+1].sum-(tr[p*2].r==tr[p*2+1].l);
}
void change(int l,int r,int x,int y,int p,int c)
{
    if (l==x&&r==y){tr[p].l=tr[p].r=c;tr[p].sum=1;tr[p].tag=c;return;}
    if (tr[p].tag!=-1)
    {
        tr[p*2].tag=tr[p].tag;
        tr[p*2].l=tr[p*2].r=tr[p].tag;
        tr[p*2+1].tag=tr[p].tag;
        tr[p*2+1].l=tr[p*2+1].r=tr[p].tag;
        tr[p*2].sum=tr[p*2+1].sum=1;
        tr[p].tag=-1;
    }
    int mid=(l+r)>>1;
    if (y<=mid) change(l,mid,x,y,p*2,c);else
    if (x>mid) change(mid+1,r,x,y,p*2+1,c);else
    {
        change(l,mid,x,mid,p*2,c);
        change(mid+1,r,mid+1,y,p*2+1,c);
    }
    tr[p].l=tr[p*2].l;
    tr[p].r=tr[p*2+1].r;
    tr[p].sum=tr[p*2].sum+tr[p*2+1].sum-(tr[p*2].r==tr[p*2+1].l);
}
void update(int u,int v,int c)
{
    while (top[u]!=top[v])
    {
        change(1,n,tid[top[u]],tid[u],1,c);
        u=f[top[u]];
    }
    change(1,n,tid[v],tid[u],1,c);
}
int ask(int l,int r,int x,int y,int p)
{
    if (l==x&&r==y) return tr[p].sum;
    if (tr[p].tag!=-1)
    {
        tr[p*2].tag=tr[p].tag;
        tr[p*2].l=tr[p*2].r=tr[p].tag;
        tr[p*2+1].tag=tr[p].tag;
        tr[p*2+1].l=tr[p*2+1].r=tr[p].tag;
        tr[p*2].sum=tr[p*2+1].sum=1;
        tr[p].tag=-1;
    }
    int mid=(l+r)>>1;
    int ans;
    if (y<=mid) ans=ask(l,mid,x,y,p*2);else
    if (x>mid) ans=ask(mid+1,r,x,y,p*2+1);else
    {
    	int tmp=1;
    	if (tr[p*2].r!=tr[p*2+1].l) tmp=0;
    	ans=ask(l,mid,x,mid,p*2)+ask(mid+1,r,mid+1,y,p*2+1)-tmp;
    }
    tr[p].l=tr[p*2].l;
    tr[p].r=tr[p*2+1].r;
    tr[p].sum=tr[p*2].sum+tr[p*2+1].sum-(tr[p*2].r==tr[p*2+1].l);
    return ans;
}
int getc(int l,int r,int p,int x)
{
    if (l==r) return tr[p].l;
    if (tr[p].tag!=-1)
    {
        tr[p*2].tag=tr[p].tag;
        tr[p*2].l=tr[p*2].r=tr[p].tag;
        tr[p*2+1].tag=tr[p].tag;
        tr[p*2+1].l=tr[p*2+1].r=tr[p].tag;
        tr[p*2].sum=tr[p*2+1].sum=1;
        tr[p].tag=-1;
    }
    int mid=(l+r)>>1;
    int color;
    if (x<=mid) color=getc(l,mid,p*2,x);else color=getc(mid+1,r,p*2+1,x);
    tr[p].l=tr[p*2].l;
    tr[p].r=tr[p*2+1].r;
    tr[p].sum=tr[p*2].sum+tr[p*2+1].sum-(tr[p*2].r==tr[p*2+1].l);
    return color;
}
int solve(int u,int v)
{
    int ans=0;
    while (top[u]!=top[v])
    {
        ans+=ask(1,n,tid[top[u]],tid[u],1);
        if (getc(1,n,1,tid[top[u]])==getc(1,n,1,tid[f[top[u]]])) ans--;
        u=f[top[u]];
    }
    ans+=ask(1,n,tid[v],tid[u],1);
    return ans;
}
int lca(int u,int v)
{
    while (top[u]!=top[v])
    {
        if (d[top[u]]<d[top[v]])swap(u,v);
        u=f[top[u]];
    }
    if (d[u]<d[v]) return u;else return v;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&col[i]);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,1,0);
    dfs(1,1);
    build(1,n,1);
    while (m--)
    {
        scanf("%s",s);
        if (s[0]=='C')
        {
            scanf("%d%d%d",&a,&b,&c);
            u=lca(a,b);
            update(a,u,c);update(b,u,c);
        }else
        {
            scanf("%d%d",&a,&b);
            u=lca(a,b);
            printf("%d\n",solve(a,u)+solve(b,u)-1);
        }
    }
}
Category: 树链剖分 | Tags:
11
27
2015
0

【bzoj3626】【LNOI2014】LCA

3626: [LNOI2014]LCA

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1045  Solved: 359
[Submit][Status][Discuss]

Description

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

 

Input

第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。

 

Output

输出q行,每行表示一个询问的答案。每个答案对201314取模输出

 

Sample Input

5 2
0
0
1
1
1 4 3
1 4 2
 

Sample Output

8
5
 

HINT

共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。

题解:我们可以先将问题转化一下,求lca的深度之和就是lca及其上面的点数之和,因此暴力可以这样打,把l~r所有点到根节点的路径上每个点的权值加1,答案就是z到根节点的路径上所有点的权值之和。然而,这时间复杂度是n3的。但这优化非常容易,我们只要把询问离线处理,l~r的答案就是(0~r)-(0~l-1),所以我们只要每次0~n-1中加n这个点,将n到根节点所有点权值加1,然后处理答案。这个用树剖轻松解决。总时间复杂度O(n*logn*logn),完美解决此题。

代码:

 

/**************************************************************
    Problem: 3626
    User: yeweining
    Language: C++
    Result: Accepted
    Time:2876 ms
    Memory:9816 kb
****************************************************************/
 
#include<cstdio>
#include<algorithm>
#include<iostream>
#define mod 201314
#define N 50010
using namespace std;
int n,q,v,edgenum,Edgenum,tot;
int vet[2*N],next[2*N],head[2*N],Vet[2*N],Next[2*N],Head[2*N],Id[2*N],Calc[2*N],x[N],y[N],z[N],ans[N];
int f[N],d[N],num[N],son[N],top[N],tid[N],pre[N];
struct node{int l,r,sum,tag;}tr[4*N];
void add(int u,int v)
{
    edgenum++;
    vet[edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
}
void Add(int u,int v,int w,int opt)
{
    Edgenum++;
    Vet[Edgenum]=v;
    Next[Edgenum]=Head[u];
    Head[u]=Edgenum;
    Id[Edgenum]=w;
    Calc[Edgenum]=opt;
}
void dfs(int u,int fa,int dep)
{
    f[u]=fa;
    d[u]=dep;
    num[u]=1;
    int maxnum=0;
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
        if (v!=fa)
        {
            dfs(v,u,dep+1);
            num[u]+=num[v];
            if (num[v]>maxnum){maxnum=num[v];son[u]=v;}
        }
    }
}
void dfs(int u,int number)
{
    top[u]=number;
    tid[u]=++tot;
    pre[tot]=u;
    if (!son[u]) return;
    dfs(son[u],number);
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
        if (v!=f[u]&&v!=son[u]) dfs(v,v);
    }
}
void build(int l,int r,int p)
{
    tr[p].l=l;tr[p].r=r;tr[p].sum=tr[p].tag=0;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,p*2);
    build(mid+1,r,p*2+1);
}
void pushdown(int p)
{
    int l=tr[p].l,r=tr[p].r;
    int mid=(l+r)>>1;
    if (l!=r)
    {
        tr[p*2].tag+=tr[p].tag;
        tr[p*2+1].tag+=tr[p].tag;
    }
    tr[p].sum+=tr[p].tag*(r-l+1);
    tr[p].sum%=mod;
    tr[p].tag=0;
}
void update(int p)
{
    pushdown(p*2);
    pushdown(p*2+1);
    tr[p].sum=tr[p*2].sum+tr[p*2+1].sum;
    tr[p].sum%=mod;
}
void change(int p,int x,int y)
{
    int l=tr[p].l,r=tr[p].r;
    pushdown(p);
    if (l==x&&y==r){tr[p].tag+=1;return;}
    int mid=(l+r)>>1;
    if (y<=mid) change(p*2,x,y);else
    if (x>mid) change(p*2+1,x,y);else
    {change(p*2,x,mid);change(p*2+1,mid+1,y);}
    update(p);
}
void update(int u,int v)
{
    while (top[u]!=top[v]) {change(1,tid[top[u]],tid[u]);u=f[top[u]];}
    change(1,tid[v],tid[u]);
}
int ask(int p,int x,int y)
{
    int l=tr[p].l,r=tr[p].r;
    pushdown(p);
    if (l==x&&y==r) return tr[p].sum;
    int mid=(l+r)>>1;
    if (y<=mid) return ask(p*2,x,y);else
    if (x>mid) return ask(p*2+1,x,y);else
    return (ask(p*2,x,mid)+ask(p*2+1,mid+1,y))%mod;
}
int get(int u,int v)
{
    int ans=0;
    while (top[u]!=top[v]) {ans+=ask(1,tid[top[u]],tid[u]);ans%=mod;u=f[top[u]];}
    ans+=ask(1,tid[v],tid[u]);
    ans%=mod;
    return ans;
}
int main()
{
    scanf("%d%d",&n,&q);
    for (int i=2;i<=n;i++)
    {
        scanf("%d",&v);
        v++;
        add(v,i);
    }
    for (int i=1;i<=q;i++)
    {
        scanf("%d%d%d",&x[i],&y[i],&z[i]);
        x[i]++;y[i]++;z[i]++;
        if (x[i]>y[i]) swap(x[i],y[i]);
        Add(x[i]-1,z[i],i,-1);Add(y[i],z[i],i,1);
    }
    dfs(1,0,1);
    dfs(1,1);
    build(1,n,1);
    for (int i=1;i<=n;i++)
    {
        update(i,1);
        for (int e=Head[i];e;e=Next[e])
        {
            int v=Vet[e];
            ans[Id[e]]+=(mod+Calc[e]*get(v,1))%mod;
            ans[Id[e]]%=mod;
        }
    }
    for (int i=1;i<=q;i++) printf("%d\n",ans[i]%mod);
}
Category: 树链剖分 | Tags:

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com