首页 > 其他分享 >OTOCI 题解

OTOCI 题解

时间:2023-06-05 17:01:47浏览次数:47  
标签:OTOCI head idx int 题解 top son fa

OTOCI

题目大意

给定 \(n\) 个带权的点,需要进行四种操作:查询两点连通性;加边;修改点权;查询两点路径的权值和。

思路分析

首先观察题目,我们会发现,在所有的操作结束后,所有的点构成一个森林,这是因为题目中的加边是建立在两点不连通的基础上的,所以不会形成任何的环,到最后自然形成了一个森林。

那么,我们可以不用理会题目的“要求在线处理所有操作”,使用离线大法跑树链剖分来解决了。

具体的说,我们先将所有的询问存下来离线处理,先用并查集维护两点的连通性和加边操作,然后在最后形成的森林上跑树剖,再依次处理每个询问。

时间复杂度:\(O(n\log^2n)\)。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=100100;//双向边

int to[N],nxt[N],head[N],w[N];
int fat[N],son[N],dep[N],siz[N],top[N],dfn[N],rnk[N];//树剖七件套
int idx,n,m,in1,in2,q,cnt;
int fa[N],inp[N*3][3];char op[15];//并查集,询问

int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}//常规并查集
void add(int u,int v){idx++;to[idx]=v;nxt[idx]=head[u];head[u]=idx;}
void Swap(int &x,int &y){int t=x;x=y;y=t;}

struct STn{int l,r,sum;};//线段树求区间和
struct ST{
    STn a[N<<2];
    void build(int p,int l,int r){
        a[p].l=l;a[p].r=r;
        if(a[p].l==a[p].r){a[p].sum=w[rnk[a[p].l]];return ;}
        int mid=(a[p].l+a[p].r)>>1;
        build(p<<1,l,mid);build(p<<1|1,mid+1,r);
        a[p].sum=a[p<<1].sum+a[p<<1|1].sum;return ;
    }
    void change(int p,int x,int k){//单点修改
        if(a[p].l==a[p].r){a[p].sum=k;return ;}
        int mid=(a[p].l+a[p].r)>>1;
        if(x<=mid) change(p<<1,x,k);else change(p<<1|1,x,k);
        a[p].sum=a[p<<1].sum+a[p<<1|1].sum;return ;
    }
    int ask(int p,int l,int r){//求和
        if(l<=a[p].l&&a[p].r<=r) return a[p].sum;
        int mid=(a[p].l+a[p].r)>>1;
        return ((l<=mid)?ask(p<<1,l,r):0)+((r>mid)?ask(p<<1|1,l,r):0);
    }
}tree;

void dfs_1(int s,int gr){//常规树剖dfs
    dep[s]=dep[gr]+1;fat[s]=gr;
    son[s]=-1;siz[s]=1;
    for(int i=head[s];i;i=nxt[i]){
        int v=to[i];
        if(v==gr) continue;
        dfs_1(v,s);
        siz[s]+=siz[v];
        if(son[s]==-1||siz[v]>siz[son[s]]) son[s]=v;
    }
}

void dfs_2(int s,int tp){
    top[s]=tp;dfn[s]=++cnt;rnk[cnt]=s;
    if(son[s]==-1) return ;
    dfs_2(son[s],tp);
    for(int i=head[s];i;i=nxt[i]){
        int v=to[i];
        if(v!=son[s]&&v!=fat[s]) dfs_2(v,v);
    }
}

int query(int x,int y){//树剖求链和
    int res=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) Swap(x,y);
        res+=tree.ask(1,dfn[top[x]],dfn[x]);
        x=fat[top[x]];
    }
    res+=tree.ask(1,min(dfn[x],dfn[y]),max(dfn[x],dfn[y]));
    return res;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        scanf("%s%d%d",op+1,&in1,&in2);
        if(op[1]=='b'){
            if(find(in1)==find(in2)) inp[i][0]=9;
            else{inp[i][0]=10;fa[find(in1)]=find(in2);add(in1,in2);add(in2,in1);}//加边
        }
        if(op[1]=='p'){inp[i][0]=1;inp[i][1]=in1;inp[i][2]=in2;}
        if(op[1]=='e'){
            if(find(in1)!=find(in2)) inp[i][0]=11;
            else{inp[i][0]=2;inp[i][1]=in1;inp[i][2]=in2;}
        }
    }
    for(int i=1;i<=n;i++)
        if(!fat[i]){dfs_1(i,0);dfs_2(i,i);}//跑多次树剖
    tree.build(1,1,n);
    for(int i=1;i<=q;i++){//离线操作
        if(inp[i][0]==9){puts("no");continue;}
        if(inp[i][0]==10){puts("yes");continue;}
        if(inp[i][0]==11){puts("impossible");continue;}
        if(inp[i][0]==1){tree.change(1,dfn[inp[i][1]],inp[i][2]);}
        if(inp[i][0]==2){cout<<query(inp[i][1],inp[i][2])<<'\n';}//回答询问
    }
    return 0;
}

标签:OTOCI,head,idx,int,题解,top,son,fa
From: https://www.cnblogs.com/TKXZ133/p/17458264.html

相关文章

  • Sell Pigs 题解
    SellPigs双倍经验题目大意有\(n\)个顾客前来买猪,共有\(m\)个猪圈,每个顾客携带着某一些猪圈的钥匙,需要买一定数量的猪。在顾客买完后,我们可以将打开的猪圈中的猪随意移动,移动完毕后所有的猪圈将关闭,直到下一个顾客到来时才能打开其拥有钥匙对应的猪圈。求最多能卖出多少猪......
  • 旅游 题解
    旅游题目大意对一颗树进行两种操作:将一条从\(u\)到\(v\)的链上的点的权值增加\(x\);查询从\(u\)到\(v\)的链上最大的\(p_i-p_j(dis_{ui}<dis_{uj})\),其中\(p_i\)表示点\(i\)的权值,\(dis_{AB}\)表示点\(A,B\)间唯一路径上边的数量。思路分析先思考,如果没有\(d......
  • Interesting Array 题解
    InterestingArray题目大意构造一个序列\(a\),使其满足若干限制条件,每个限制条件是形如lrq的式子,其意义是:\(\&_{i=l}^ra_i=q\)。题意分析看上去是构造题,实际上是数据结构题。我们不妨先令初始时\(a\)为一个全\(0\)序列,再逐一看每个限制条件。为了满足某一个限制条件......
  • Sum of MSLCM 题解
    SumofMSLCM题目大意定义\(\text{MSLCM}(n)\)为所有满足该数集的\(\text{lcm}\)为\(n\)的数集中元素个数最多的数集的所有数字的和,现有多次询问,求\[\sum_{i=2}^n\text{MSLCM}(i)\]思路分析大水题。虽然看着这个东西很可怕,但仔细一想你就会发现,其实\(\text{MSLCM}(n)......
  • Java模拟表单提交编码不同导致乱码问题解决
    最近有个业务需要模拟表单提交到asp页面中,但是我的项目编码是UTF8,而asp页面是GB2312,中文字段提交后,到达数据库后是乱码.问题的解决在于模拟提交的时候指定编码:我用的HTTP框架是Unirest,代码如下:......
  • 安装Navicat遇到的问题解决
    1、如果遇到安装出现问题,并且不能激活,需要重新卸载安装。需要彻底卸载2、除了点击卸载安装之后,需要注册表删除掉所有的信息,以及删除掉在C:\ProgramFiles\PremiumSoft的Navicat删除掉3、删除注册表Win+R之后输入:regedit进入注册表3.1找到计算机\HKEY_CURRENT_USER\Softwar......
  • [ABC208E] Digit Products 题解
    DigitProducts题目大意求有多少个不大于\(n\)的正整数,使得该正整数各位乘积不大于\(k\)。思路分析观察数据范围,首先考虑数位DP。考虑设计记忆化搜索函数dfs(intpos,boollimit,boollead0,intmul)表示当前枚举到第\(\text{pos}\)位,第\(\text{pos}\)位是否受到限......
  • [ABC207E] Mod i 题解
    Modi题目大意给定一个序列\(a\),问将其划分成若干段,满足第\(i\)段的和是\(i\)的倍数的划分方案的个数。思路分析考虑DP,设\(f_{i,j}\)表示将序列中前\(i\)个数划分成\(j\)段,且满足条件的划分方案的个数,容易得出状态转移方程:\[f_{i,j}=\sumf_{k,j-1}(\sum_{h=k+1}......
  • [ABC205E] White and Black Balls 题解
    WhiteandBlackBalls题目大意将\(n\)个白球,\(m\)个黑球排成一列,要求满足\(\foralli\in[1,n+m],w_i\leb_i+k\),问存在多少种排法。其中\(w_i\)表示第\(i\)个球前的白球数量,\(b_i\)表示第\(i\)个球前的黑球数量。思路分析我们可以将一种排法映射成一条从\((0,0)......
  • [ABC205F] Grid and Tokens 题解
    GridandTokens题目大意给定\(n\)个点和一个\(H\timesW\)的网格,每个点可以放置在\((A_i,B_i)\)到\((C_i,D_i)\)的矩形中或不放,每一行或一列只能放置一个点,求最多能放多少个点。思路分析首先看数据范围,再结合题目给的限制条件,容易发现这是一道网络流。考虑建图,因为......