各位大神都用网络流啊 最短路啊解这道题,那么既然是可以求最短路,为什么不可以从树上跑呢?

怀着这种想法,我冥思苦想(划掉),发现,###我可以用LCA做这道题啊~

然而鄙人不才,什么Tarjan啊ST表啊都不会,只会用一个倍增来求LCA,所以权当抛砖引玉吧。

不过我估计应该没什么想学LCA的来这道题看LCA题解吧。所以多半是写着玩~~

###先说说思路(这还用说?):

###建一个有三个节点的树,1为树根,2 3分别是左右儿子。

###其中1 2之间的距离为a,2 3之间的距离为b,然后求1 2的LCA,和分别到LCA的距离,加起来就是1->3的最短路

###其实就是题目中所求的a+b了

好吧闲话不说 上代码了(多半是个LCA的板子了):

#include<cstdio>                                                  //头文件
#define NI 2                                                          
//从来不喜欢算log所以一般用常数 不知道算不算坏习惯 因为3个节点 所以log3(当然以2为底)上取整得2
struct edge
{
    int to,next,data;                                              //分别表示边的终点,下一条边的编号和边的权值
}e[30];                                                                     //邻接表,点少边少开30是为了浪啊
int v[10],d[10],lca[10][NI+1],f[10][NI+1],tot=0;      //数组开到10依然为了浪
//数组还解释嘛,v表示第一条边在邻接表中的编号,d是深度,lca[x][i]表示x向上跳2^i的节点,f[x][i]表示x向上跳2^i的距离和
void build(int x,int y,int z)                                      //建边
{
    e[++tot].to=y; e[tot].data=z; e[tot].next=v[x]; v[x]=tot;
    e[++tot].to=x; e[tot].data=z; e[tot].next=v[y]; v[y]=tot;
}
void dfs(int x)                                                        //递归建树
{
    for(int i=1;i<=NI;i++)                                   //懒,所以常数懒得优化
        f[x][i]=f[x][i-1]+f[lca[x][i-1]][i-1],
        lca[x][i]=lca[lca[x][i-1]][i-1];                   //建树的同时进行预处理
    for(int i=v[x];i;i=e[i].next)                              //遍历每个连接的点
    {
        int y=e[i].to;
        if(lca[x][0]==y) continue;
        lca[y][0]=x;                                       //小技巧:lca[x][0]即为x的父亲~~(向上跳2^0=1不就是父节点嘛)
        f[y][0]=e[i].data;
        d[y]=d[x]+1;
        dfs(y);                                            //再以这个节点为根建子树【这里真的用得到嘛??】
    }
}
int ask(int x,int y)                                             //询问,也是关键
{                                                                        
    if(d[x]<d[y]) {int t=x;x=y;y=t;}                  //把x搞成深的点
    int k=d[x]-d[y],ans=0;
    for(int i=0;i<=NI;i++)
        if(k&(1<<i))                                      //若能跳就把x跳一跳
            ans+=f[x][i],                              //更新信息
            x=lca[x][i];
    for(int i=NI;i>=0;i--)                                  //不知道能不能正着循环,好像倒着优,反正记得倒着就好了
        if(lca[x][i]!=lca[y][i])                            //如果x跳2^i和y跳2^j没跳到一起就让他们跳
            ans+=f[x][i]+f[y][i],
            x=lca[x][i],y=lca[y][i];
    return ans+f[x][0]+f[y][0];                           //跳到LCA上去(每步跳的时候都要更新信息,而且要在跳之前更新信息哦~)
}
int main()
{
    int a,b;
    scanf("%d%d",&a,&b);
    build(1,2,a);
    build(1,3,b);                                                       //分别建1 2、1 3之间的边
    dfs(1);                                                                //以1为根建树
    printf("%d",ask(2,3));                                         //求解2 3到它们的LCA的距离和并输出
}

看来还没人用Splay,赶紧水一发

因为加法满足交换律,所以我们可以把序列翻转一下,所求的总和不变

序列反转自然而然就能想到Splay啦

代码送上

//一颗资瓷区间加、区间翻转、区间求和的Splay
#include <bits/stdc++.h>
#define ll long long
#define N 100000
using namespace std;
int sz[N], rev[N], tag[N], sum[N], ch[N][2], fa[N], val[N];
int n, m, rt, x;
void push_up(int x){
    sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
    sum[x] = sum[ch[x][1]] + sum[ch[x][0]] + val[x];
}
void push_down(int x){
    if(rev[x]){
        swap(ch[x][0], ch[x][1]);
        if(ch[x][1]) rev[ch[x][1]] ^= 1;
        if(ch[x][0]) rev[ch[x][0]] ^= 1;
        rev[x] = 0;
    }
    if(tag[x]){
        if(ch[x][1]) tag[ch[x][1]] += tag[x], sum[ch[x][1]] += tag[x];
        if(ch[x][0]) tag[ch[x][0]] += tag[x], sum[ch[x][0]] += tag[x];
        tag[x] = 0;
    }
}
void rotate(int x, int &k){
    int y = fa[x], z = fa[fa[x]];
    int kind = ch[y][1] == x;
    if(y == k) k = x;
    else ch[z][ch[z][1]==y] = x;
    fa[x] = z; fa[y] = x; fa[ch[x][!kind]] = y;
    ch[y][kind] = ch[x][!kind]; ch[x][!kind] = y;
    push_up(y); push_up(x);
}
void splay(int x, int &k){
    while(x != k){
        int y = fa[x], z = fa[fa[x]];
        if(y != k) if(ch[y][1] == x ^ ch[z][1] == y) rotate(x, k);
        else rotate(y, k);
        rotate(x, k);
    }
}
int kth(int x, int k){
    push_down(x);
    int r = sz[ch[x][0]]+1;
    if(k == r) return x;
    if(k < r) return kth(ch[x][0], k);
    else return kth(ch[x][1], k-r);
}
void split(int l, int r){
    int x = kth(rt, l), y = kth(rt, r+2);
    splay(x, rt); splay(y, ch[rt][1]);
}
void rever(int l, int r){
    split(l, r);
    rev[ch[ch[rt][1]][0]] ^= 1;
}
void add(int l, int r, int v){
    split(l, r);
    tag[ch[ch[rt][1]][0]] += v;
    val[ch[ch[rt][1]][0]] += v;
    push_up(ch[ch[rt][1]][0]);
}
int build(int l, int r, int f){
    if(l > r) return 0;
    if(l == r){
        fa[l] = f;
        sz[l] = 1;
        return l;
    }
    int mid = l + r >> 1;
    ch[mid][0] = build(l, mid-1, mid);
    ch[mid][1] = build(mid+1, r, mid);
    fa[mid] = f;
    push_up(mid);
    return mid;
}
int asksum(int l, int r){
    split(l, r);
    return sum[ch[ch[rt][1]][0]];
}
int main(){
    //总共两个数
    n = 2;
    rt = build(1, n+2, 0);//建树
    for(int i = 1; i <= n; i++){
        scanf("%d", &x);
        add(i, i, x);//区间加
    }
    rever(1, n);//区间翻转
    printf("%d\n", asksum(1, n));//区间求和
    return 0;
}

终于可以写一份A+B这么难的题的题解了。

咦?竟然没有人写LCT的题解?

Link-Cut Tree 会很伤心的!

ORZ为了不让LCT伤心于是我来一份LCT的A+B题解吧!

送上代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
using namespace std;
struct node 
{
    int data,rev,sum;
    node *son[2],*pre;
    bool judge();
    bool isroot();
    void pushdown();
    void update();
    void setson(node *child,int lr);
}lct[233];
int top,a,b;
node *getnew(int x)
{
    node *now=lct+ ++top;
    now->data=x;
    now->pre=now->son[1]=now->son[0]=lct;
    now->sum=0;
    now->rev=0;
    return now;
}
bool node::judge(){return pre->son[1]==this;}
bool node::isroot()
{
    if(pre==lct)return true;
    return !(pre->son[1]==this||pre->son[0]==this);
}
void node::pushdown()
{
    if(this==lct||!rev)return;
    swap(son[0],son[1]);
    son[0]->rev^=1;
    son[1]->rev^=1;
    rev=0;
}
void node::update(){sum=son[1]->sum+son[0]->sum+data;}
void node::setson(node *child,int lr)
{
    this->pushdown();
    child->pre=this;
    son[lr]=child;
    this->update();
}
void rotate(node *now)
{
    node *father=now->pre,*grandfa=father->pre;
    if(!father->isroot()) grandfa->pushdown();
    father->pushdown();now->pushdown();
    int lr=now->judge();
    father->setson(now->son[lr^1],lr);
    if(father->isroot()) now->pre=grandfa;
    else grandfa->setson(now,father->judge());
    now->setson(father,lr^1);
    father->update();now->update();
    if(grandfa!=lct) grandfa->update();
}
void splay(node *now)
{
    if(now->isroot())return;
    for(;!now->isroot();rotate(now))
    if(!now->pre->isroot())
    now->judge()==now->pre->judge()?rotate(now->pre):rotate(now);
}
node *access(node *now)
{
    node *last=lct;
    for(;now!=lct;last=now,now=now->pre)
    {
        splay(now);
        now->setson(last,1);
    }
    return last;
}
void changeroot(node *now)
{
    access(now)->rev^=1;
    splay(now);
}
void connect(node *x,node *y)
{
    changeroot(x);
    x->pre=y;
    access(x);
}
void cut(node *x,node *y)
{
    changeroot(x);
    access(y);
    splay(x);
    x->pushdown();
    x->son[1]=y->pre=lct;
    x->update();
}
int query(node *x,node *y)
{
    changeroot(x);
    node *now=access(y);
    return now->sum;
}
int main()
{
    scanf("%d%d",&a,&b);
    node *A=getnew(a);
    node *B=getnew(b);
    //连边 Link
        connect(A,B);
    //断边 Cut
        cut(A,B);
    //再连边orz Link again
        connect(A,B);
    printf("%d\n",query(A,B)); 
    return 0;
}

5 条评论

  • 佬大orz

  • qp

  • 从哪找来的

  • #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <cctype>
    #include <climits>
    #include <algorithm>
    #include <map>
    #include <queue>
    #include <vector>
    #include <ctime>
    #include <string>
    #include <cstring>
    using namespace std;
    const int N=405;
    struct Edge {
        int v,w;
    };
    vector<Edge> edge[N*N];
    int n;
    int dis[N*N];
    bool vis[N*N];
    struct cmp {
        bool operator()(int a,int b) {
            return dis[a]>dis[b];
        }
    };
    int Dijkstra(int start,int end)
    {
        priority_queue<int,vector<int>,cmp> dijQue;
        memset(dis,-1,sizeof(dis));
        memset(vis,0,sizeof(vis));
        dijQue.push(start);
        dis[start]=0;
        while(!dijQue.empty()) {
            int u=dijQue.top();
            dijQue.pop();
            vis[u]=0;
            if(u==end)
                break;
            for(int i=0; i<edge[u].size(); i++) {
                int v=edge[u][i].v;
                if(dis[v]==-1 || dis[v]>dis[u]+edge[u][i].w) {
                    dis[v]=dis[u]+edge[u][i].w;
                    if(!vis[v]) {
                        vis[v]=true;
                        dijQue.push(v);
                    }
                }
            }
        }
        return dis[end];
    }
    int main()
    {
        int a,b;
        scanf("%d%d",&a,&b);
        Edge Qpush;
        
        Qpush.v=1;
        Qpush.w=a;
        edge[0].push_back(Qpush);
        
        Qpush.v=2;
        Qpush.w=b;
        edge[1].push_back(Qpush);
        
        printf("%d",Dijkstra(0,2));
        return 0;
    }
    
  • //一颗资瓷区间加、区间翻转、区间求和的Splay
    #include <bits/stdc++.h>
    #define ll long long
    #define N 100000
    using namespace std;
    int sz[N], rev[N], tag[N], sum[N], ch[N][2], fa[N], val[N];
    int n, m, rt, x;
    void push_up(int x){
        sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
        sum[x] = sum[ch[x][1]] + sum[ch[x][0]] + val[x];
    }
    void push_down(int x){
        if(rev[x]){
            swap(ch[x][0], ch[x][1]);
            if(ch[x][1]) rev[ch[x][1]] ^= 1;
            if(ch[x][0]) rev[ch[x][0]] ^= 1;
            rev[x] = 0;
        }
        if(tag[x]){
            if(ch[x][1]) tag[ch[x][1]] += tag[x], sum[ch[x][1]] += tag[x];
            if(ch[x][0]) tag[ch[x][0]] += tag[x], sum[ch[x][0]] += tag[x];
            tag[x] = 0;
        }
    }
    void rotate(int x, int &k){
        int y = fa[x], z = fa[fa[x]];
        int kind = ch[y][1] == x;
        if(y == k) k = x;
        else ch[z][ch[z][1]==y] = x;
        fa[x] = z; fa[y] = x; fa[ch[x][!kind]] = y;
        ch[y][kind] = ch[x][!kind]; ch[x][!kind] = y;
        push_up(y); push_up(x);
    }
    void splay(int x, int &k){
        while(x != k){
            int y = fa[x], z = fa[fa[x]];
            if(y != k) if(ch[y][1] == x ^ ch[z][1] == y) rotate(x, k);
            else rotate(y, k);
            rotate(x, k);
        }
    }
    int kth(int x, int k){
        push_down(x);
        int r = sz[ch[x][0]]+1;
        if(k == r) return x;
        if(k < r) return kth(ch[x][0], k);
        else return kth(ch[x][1], k-r);
    }
    void split(int l, int r){
        int x = kth(rt, l), y = kth(rt, r+2);
        splay(x, rt); splay(y, ch[rt][1]);
    }
    void rever(int l, int r){
        split(l, r);
        rev[ch[ch[rt][1]][0]] ^= 1;
    }
    void add(int l, int r, int v){
        split(l, r);
        tag[ch[ch[rt][1]][0]] += v;
        val[ch[ch[rt][1]][0]] += v;
        push_up(ch[ch[rt][1]][0]);
    }
    int build(int l, int r, int f){
        if(l > r) return 0;
        if(l == r){
            fa[l] = f;
            sz[l] = 1;
            return l;
        }
        int mid = l + r >> 1;
        ch[mid][0] = build(l, mid-1, mid);
        ch[mid][1] = build(mid+1, r, mid);
        fa[mid] = f;
        push_up(mid);
        return mid;
    }
    int asksum(int l, int r){
        split(l, r);
        return sum[ch[ch[rt][1]][0]];
    }
    int main(){
        //总共两个数
        n = 2;
        rt = build(1, n+2, 0);//建树
        for(int i = 1; i <= n; i++){
            scanf("%d", &x);
            add(i, i, x);//区间加
        }
        rever(1, n);//区间翻转
        printf("%d\n", asksum(1, n));//区间求和
        return 0;
    }
    
  • 1

信息

ID
1190
难度
2
分类
(无)
标签
递交数
443
已通过
272
通过率
61%
上传者