数据结构[刷水]

1.POJ The merchant
给定一棵树,每个点有一个点权
每次询问(u,v),求从u到v的路径上,a点权值-b点权值最大
要求a点比b点更靠近(或者等于)v
1 ≤ N, wi, Q ≤ 50000
时限3秒
题解:树链剖分,线段树维护最大最小值,以及当前区间答案。
因为树链剖分是从上往下的,所以区间的左右就变成了树的上和下。
然后方向反了调了一晚上QAQ

#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define v edge[i].to
#define RepG(i,x) for(int i = head[x];~ i;i = edge[i].next)
#define u t[x]
#define o t[y]
#define ulfc t[x << 1]
#define urtc t[x << 1 | 1]
#define lson x << 1,l,mid
#define rson x << 1 | 1,mid + 1,r
#define RT 1,1,n
#define mid (l + r >> 1)
using namespace std;
const int N = 50005;
const int inf = 1 << 29;
int Rank[N],tid[N],sz[N],fa[N],dep[N],top[N],ql,qr,n,m,w[N],head[N],cnt,son[N],tim;
struct Edge{int next,to;}edge[N << 1];
void save(int a,int b)
{
    edge[cnt] = (Edge){head[a],b},head[a] = cnt ++;
    edge[cnt] = (Edge){head[b],a},head[b] = cnt ++;
}
struct Seg{int mx,mn,ml,mr;Seg(){mx = ml = mr = -inf;mn = inf;}}t[N << 2];
void Upd(int x){
    u.mx = max(ulfc.mx,urtc.mx);
    u.mn = min(ulfc.mn,urtc.mn);
    u.ml = max(ulfc.ml,max(urtc.ml,urtc.mx - ulfc.mn)); //从左开始走 
    u.mr = max(ulfc.mr,max(urtc.mr,ulfc.mx - urtc.mn)); //从右开始走 
}
Seg Merge(Seg lfc,Seg rtc)
{
    Seg uu = Seg();
    uu.mx = max(lfc.mx,rtc.mx);
    uu.mn = min(lfc.mn,rtc.mn);
    uu.ml = max(lfc.ml,max(rtc.ml,rtc.mx - lfc.mn)); // 从左开始走 
    uu.mr = max(lfc.mr,max(rtc.mr,lfc.mx - rtc.mn)); // 从右开始走 
    return uu;
}
void dfs(int x) // rt
{
    sz[x] = 1;
    RepG(i,x)
        if(v != fa[x]){
            fa[v] = x,dep[v] = dep[x] + 1,dfs(v);
            sz[x] += sz[v];
            if(!son[x] || sz[son[x]] < sz[v])
                son[x] = v;
        }
}
void dfs(int x,int tp)
{
    top[x] = tp,Rank[tid[x] = ++ tim] = x;
    if(!son[x])return;
    dfs(son[x],tp);
    RepG(i,x)if(v != fa[x] && v != son[x])dfs(v,v);
}
Seg Rev(Seg x){swap(x.ml,x.mr);return x;}
void Build(int x,int l,int r)
{
    if(l == r){u.ml = u.mr = 0,u.mx = u.mn = w[Rank[l]];return;}
    Build(lson),Build(rson);
    Upd(x);
}
Seg Qry(int x,int l,int r)
{
    if(ql <= l && r <= qr)
        return u;
    Seg a = Seg(),b = Seg();
    if(ql <= mid)a = Qry(lson);
    if(mid < qr)b = Qry(rson);
    return Merge(a,b);
}
void Query(int x,int y)
{
    Seg Qx = Seg(),Qy = Seg();
    int cpt = 1;
    while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]])
        {
            ql = tid[top[y]],qr = tid[y];
            Qy = Merge(Qry(RT),Qy);
            y = fa[top[y]];

        }
        else 
        {
            ql = tid[top[x]],qr = tid[x];
            Qx = Merge(Qx,Rev(Qry(RT)));
            x = fa[top[x]];
        }cpt ++;
    }
    if(dep[x] > dep[y]){
        ql = tid[y],qr = tid[x];
        Qx = Merge(Qx,Rev(Qry(RT)));
    }
    else{
        ql = tid[x],qr = tid[y];
        Qy = Merge(Qry(RT),Qy);
    }
    printf("%d\n",Merge(Qx,Qy).ml);
}
int main (){
    while(~scanf("%d",&n))
    {
        memset(head,-1,sizeof(head));
        Rep(i,n)scanf("%d",&w[i]);
        Rep(i,n - 1){int a,b;scanf("%d%d",&a,&b);save(a,b);}
        dfs(1),dfs(1,1);
        fa[1] = 1;
        Build(RT);
        scanf("%d",&m);
        Rep(i,m){int a,b;scanf("%d%d",&a,&b);Query(a,b);}
    }
    return 0;
}

2.HDU Monkey King
有n个点,每个点有一个权值,有m个操作
每次操作,询问u和v所在联通块权值最大的点x,y,输出最大值,然后将x和y的权值减半,将u和v之间连一条边
n,m<=10^5
题解:感觉线段树合并可以搞,但是正解是左偏树(我太弱不会QAQ)
//正在写QAQ
//写完了QAQ

#include <cstdio>
#include <cstring>
#include <algorithm>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define RepG(i,x) for(int i = head[x];~ i ;i = edge[i].next)
#define v edge[i].to
#define u t[x]
#define o t[y]
#define lc ch[0]
#define rc ch[1]
#define urtc t[u.rc]
#define ulfc t[u.lc]
using namespace std;
const int N = 100005;
struct LT{int ch[2],w,d;LT(){lc = rc = w = d = 0;}}t[N];
int fa[N],n,m;
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
int Merge(int x,int y)
{
    if(!x || !y)return x + y;
    if(u.w < o.w)swap(x,y);
    u.rc = Merge(u.rc,y);
    fa[u.rc] = x;
    if(urtc.d > ulfc.d)swap(u.lc,u.rc);
    if(!u.rc)u.d = 0;
    else u.d = urtc.d + 1;
    return x;
}
int Del(int x){
    fa[u.lc] = u.lc,fa[u.rc] = u.rc;
    int y = Merge(u.lc,u.rc);
    u.lc = u.rc = u.d = 0;
    return y;
}
void solve(int x,int y)
{   
    u.w /= 2,o.w /= 2;
    int l = Del(x),r = Del(y);
    l = Merge(l,x),r = Merge(r,y);
    l = Merge(l,r);
    printf("%d\n",t[l].w);
}
int main ()
{
    while(~scanf("%d",&n)){
        Rep(x,n)scanf("%d",&u.w),fa[x] = x,u.d = u.rc = u.lc = 0;
        scanf("%d",&m);
        Rep(i,m)
        {
            int tx,ty;
            scanf("%d%d",&tx,&ty);
            int x = find(tx),y = find(ty);
            if(x != y)solve(x,y);
            else puts("-1");
        }
    }
    return 0;
}

HDU自带多组数据真的烦QAQ
3.弹飞绵羊
有n个节点排成一排,每个节点有一个属性ki(ki>0,ki是整数)
第i个节点可以达到第i+ki个节点,如果i+ki>n,则可以到达终点
两种操作:
1.询问第i个点到达终点需要多少步
2.修改第i个节点的ki值
n<=200000,m<=100000
题解:LCT裸题。
4.Snacks
有一颗n个节点的有根树,每个节点有一个权值,两种操作
1.修改某一个点的权值
2.寻找一条从根出发的路径,路径必须经过x节点,要求权值和最大
1≤n,m≤100000
题解:注意到经过x节点就是经过x的子树任意一点。
dfs序 + 线段树可以搞掉。树链剖分当然更支持了QAQ
主要介绍一下dfs序的使用方法:
首先前缀和。那么对于某个节点的修改,就修改它的子树。
对于某个根到x的询问,那就直接询问st[x] - > ed[x]这一段的最大值。
多组询问什么的忘记清空标记简直不要太爽。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cstring>
#include <algorithm>
#define RT 1,1,n
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define RepG(i,x) for(int i = head[x];~ i;i = edge[i].next)
#define v edge[i].to
#define u t[x]
#define ulfc t[x << 1]
#define urtc t[x << 1 | 1]
#define mid (l + r >> 1)
#define lson x << 1,l,mid
#define rson x << 1 | 1,mid + 1,r
using namespace std;
const int N = 100005;
const long long inf = 1e15;
typedef long long ll;
struct Edge{int next,to; Edge(int next=0, int to=0):next(next), to(to) {}}edge[N << 1];
struct Tree{ll sum,tag;Tree(){sum = tag = 0;}}t[N << 4];
int tid[N],Rank[N],fa[N],ed[N],tim,cnt,head[N],n,m,ql,qr;
ll sum[N],val[N];
void save(int a,int b){edge[cnt] = Edge(head[a],b);head[a] = cnt ++;}
void dfs(int x){Rank[tid[x] = ++ tim] = x;RepG(i,x)if(v != fa[x])fa[v] = x,sum[v] = sum[x] + val[v],dfs(v);ed[x] = tim;}
void Upd(int x){u.sum = max(ulfc.sum,urtc.sum);}
void Dw(int x){if(u.tag)ulfc.sum += u.tag,urtc.sum += u.tag,ulfc.tag += u.tag,urtc.tag += u.tag,u.tag = 0;}
void Build(int x,int l,int r){if(l == r){u.sum = sum[Rank[l]];u.tag = 0;return;}Build(lson),Build(rson),Upd(x),u.tag = 0;}
void Modify(int x,int l,int r,ll tag)
{
    Dw(x);
    if(l >= ql && r <= qr){u.sum += tag,u.tag += tag;return;}
    if(ql <= mid)Modify(lson,tag);
    if(mid < qr)Modify(rson,tag);
    Upd(x);
}
ll Qry(int x,int l,int r)
{
    Dw(x);
    if(l >= ql && r <= qr)return u.sum;
    ll lx = -inf,rx = -inf;
    if(ql <= mid)lx = Qry(lson);
    if(mid < qr)rx = Qry(rson);
    return Upd(x),max(lx,rx);
}
int main ()
{
    int T;
    scanf("%d",&T);
    Rep(Case,T){
        printf("Case #%d:\n",Case); 
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        cnt = tim = 0;
        Rep(i,n - 1){int a,b;scanf("%d%d",&a,&b);a ++,b ++;save(a,b),save(b,a);}
        Rep(i,n)scanf("%lld",&val[i]),sum[i] = val[i];
        dfs(1);
        Build(RT);
        Rep(i,m)
        {
            int a,b;ll c;
            scanf("%d",&a);
            if(a & 1 ^ 1)
            {
                scanf("%d%lld",&b,&c),b ++;
                ql = tid[b],qr = ed[b];
                ll cc = c - val[b];
                val[b] = c;
                Modify(RT,cc);
            }
            else 
            {
                scanf("%d",&b);
                b ++; 
                ql = tid[b],qr = ed[b];
                printf("%lld\n",Qry(RT));
            }
        }
    }
    return 0;
}

5.【FOTILE模拟赛】L
给定N个正整数
对于一个询问(l,r),你需要求出max(Ai xor Ai+1 xor Ai+2 … xor Aj),其中l<=i<=j<=r
N=12000,M=6000
强制在线
题解:这题好像不是很好想,现在不是很会。
分块应该可以做,然后搞一个可持久化Trie就好了。
6.SDOI2014方伯伯的玉米田
给定n个数,你可以最多进行k次操作
每次操作可以指定一段区间(l,r),将区间内的每个数加1
要求最后的序列的最长非降子序列最长
1 < N < 10000,1 < K ≤ 500,1 ≤ ai ≤5000
时限6秒
题解:
首先肯定是dp对吧。
f[i][j]表示强制第i个,当前还有j次操作的长度。

f[i][j]=f[k][j]+1(a[k]<=a[i])

f[i][j]=f[k][j+a[i]a[k]](a[k]>a[i])+1

枚举k的话复杂度是O(n ^ 2)的。
树状数组优化即可。
7.脑洞题
N个点,一开始没有边,两种操作
1.在u和v之间加一条边
2.在第k次操作之前,u和v有没有联在一起
n,m=10^5
强制在线
题解:并查集 + 按秩合并。
8.Color the Ball
有2^31-1个球排成一排,一开始全是白色,有3种操作
1.将一段区间的球全部刷成白色
2.将一段区间的球全部刷成黑色
3.询问某区间内最长的连续黑色球数量
操作数<=10^5
强制在线
题解:动态开点线段树。
9.遥远的国度
一颗包含n个点的有根树,每个点都有一个权值
1.把u设成根
2.将u到v路径上所有点的权值设为k
3.询问以u为根的子树的最小权值
n<=100000,m<=100000
题解:fsf给我讲过QAQ
考虑后两个树链剖分瞎搞一点问题都没有。
但是换根的话,仔细一想实际上就只需要移动子树信息就好了,根本不需要换根。随便分类讨论是可做的。
10.CF 163E
不好意思我不会AC自动机QAQ
11.阿狸的打字机
为什么还是AC自动机QAQ
12.Spoj 10628. Count on a tree
给定一颗树,树上每个点有权值
每次询问,u到v路径上权值第k大的值是多少
强制在线
N,M<=100000
题解:主席树随便搞。
13.[SDOI2013]森林
给定n个节点,每个节点有一个权值,两种操作
1.询问u到v路径上权值第k小的值是多少,保证u和v联通
2.在u和v之间添加一条边,保证之前u和v不连通
强制在线
N,M<=8*10^4
题解:启发式合并主席树……?
虽然很多东西都不是很会写。 但是应该是能A的
QAQ

你可能感兴趣的:(数据结构[刷水])