C. 【lxs Contest #140】Cac

【题意】

【题解】

仙人掌,考虑圆方树。

正常构建圆方树并在每两个树边之间加入方点。

考虑使用每个方点维护与他相连的圆点信息,发现每个方点只能维护他的儿子圆点信息,否则会算重。

题目中修改操作即将两个圆点在新树上的路径上方点都加上v,即表示路径上所有环上的圆点都加v。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=6e5+5;
const int mo=998244353;
int n,m,q,last[N],size,dfn[N],low[N],siz,dui[N],ans[N],size1,last1[N],dax[N],f[N][20],len,cnt,st[N],toptop,Dfn[N],gaga;
struct pigu
{
    int dao,ne;
}a[N<<3],b[N<<2];
inline void lingjiebiao(int x,int y)
{
    a[++size].dao=y;
    a[size].ne=last[x];
    last[x]=size;
}
inline void lingjiebiao2(int x,int y)
{
    b[++size1].dao=y;
    b[size1].ne=last1[x];
    last1[x]=size1;
}
inline int jia(int x,int y)
{
    return (((x+y)%mo)+mo)%mo;
}
inline int read()
{
    char c=getchar();
    int x=0,f=1;
    while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return x*f; 
}
inline void tarjan(int now,int fa)
{
    dfn[now]=low[now]=++cnt;
    st[++toptop]=now;
    for(int i=last[now];i;i=a[i].ne)
    {
        if(dfn[a[i].dao]==0)
        {
            tarjan(a[i].dao,now);
            low[now]=min(low[now],low[a[i].dao]);
            if(low[a[i].dao]==dfn[now])
            {
                int k;
                ++siz;
                do
                {
                    k=st[toptop--];
                    lingjiebiao2(k,siz);
                    lingjiebiao2(siz,k);
                }while(k!=a[i].dao);
                lingjiebiao2(now,siz);
                lingjiebiao2(siz,now); 
            }
        }
        else
            low[now]=min(low[now],dfn[a[i].dao]);
    }
}
int dep[N],sh[N],Siz[N];
inline int lowbit(int x)
{
    return x&(-x);
}
inline void modify(int zai,int zhi)
{
    for(int i=zai;i<=siz;i+=lowbit(i)) sh[i]=jia(sh[i],zhi);
}
inline int dquery(int x)
{
    int ha=0;
    for(int i=x;i;i-=lowbit(i)) ha=jia(ha,sh[i]);
    return ha;
}
inline int query(int x)
{
    return dquery(Dfn[x]+Siz[x]-1)-dquery(Dfn[x]-1)+mo;
}
inline void dfs(int now,int fa)
{
    f[now][0]=fa;dep[now]=dep[fa]+1;Dfn[now]=++gaga;Siz[now]=1;
    for(int i=1;f[f[now][i-1]][i-1];i++) f[now][i]=f[f[now][i-1]][i-1];
    for(int i=last1[now];i;i=b[i].ne)
    {
        if(b[i].dao==fa) continue;
        dfs(b[i].dao,now);
        Siz[now]+=Siz[b[i].dao];
    }
}
inline int get_lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=19;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
    if(x==y) return x;
    for(int i=19;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
signed main()
{
//    freopen("cac.in","r",stdin);
    //freopen("cac.out","w",stdout);
    n=read();m=read();q=read();
    for(int i=1,x,y;i<=m;i++)
    {
        x=read();y=read();
        lingjiebiao(x,y);
        lingjiebiao(y,x);
    }siz=n;
    tarjan(1,0);
    dfs(1,0);
    for(int i=1,x,y,z,v;i<=q;i++)
    {
        x=read();y=read();
        if(x==0)
        {
            z=read();v=read();
            int lca=get_lca(y,z);
            modify(Dfn[y],v);
            modify(Dfn[z],v);
            modify(Dfn[lca],mo-v);
            if(f[lca][0]) modify(Dfn[f[lca][0]],mo-v);
            if(lca>n) ans[f[lca][0]]=jia(ans[f[lca][0]],v);
            else ans[lca]=jia(ans[lca],v);
        }
        else
        {
            int daan=jia(ans[y],f[y][0]?query(f[y][0]):0);
            cout<<daan<<"\n";
        }
    }
}
View Code

注意如果lca是圆点,则需将该圆点答案单独加上v,因为维护他的方点没法加。

查询即查询该点本身及他的父亲方点的权值即可,使用树链剖分或树上差分即可。

代码如下:

猜你喜欢

转载自www.cnblogs.com/betablewaloot/p/12210697.html