ABC357 基环树dp|懒标记线段树

E

有一堆(i,ai)(i,a_i)(i,ai)这样的有向边,问图上有多少对(u,v)(u,v)(u,v),存在路径u−vu-vu−v。

首先这个边的形式,意味着这是个内向基环树,所有点都能到达一个环上。缩点之后,就是计算新图上每个点,有多少个点能到达这个点。这可以拓扑排序dpdpdp。然后每个点的贡献就是szi∗dpisz_i*dp_iszi∗dpi,dpdpdp结果乘上新图上这个点在旧图上的点数,这是为了处理环有多个点的情况,环上点到达一个就能到达其他

c 复制代码
int in[N],dfn[N],co[N],low[N],s[N],ind[N],top,cnt,tot;
vector<int>a[N],b[N];
inline void tarjan(int x){
    dfn[x]=low[x]=++cnt;
    s[++top]=x;
    ind[x]=1;
    for(int v:a[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(ind[v]){
            low[x]=min(low[x],low[v]);
        }
    }
    if(low[x]==dfn[x]){
        ++tot;
        while(1){
            int X=s[top--];
            co[X]=tot;
            ind[X]=0;
            if(!(x^X)){
                break;
            }
        }
    }
}
int sz[N],w[N];
void solve()
{
	int n;
	cin>>n;
	rep(i,1,n){
		int x;
		cin>>x;
		a[x].push_back(i);
	}
	rep(i,1,n){
		if(!dfn[i]){
			tarjan(i);
		}
	}
	
	rep(i,1,n){
		sz[co[i]]++;
		w[co[i]]++;
		for(int v:a[i]){
			if(co[i]^co[v]){
				b[co[i]].push_back(co[v]);
				in[co[v]]++;
			}
		}
	}
	
	queue<int>q;
	rep(i,1,tot){
		if(!in[i]){
			q.push(i);
		}
	}
	
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int v:b[u]){
			w[v]+=w[u];
			if(--in[v]==0){
				q.push(v);
			}
		}
	}
	int ans=0;
	rep(i,1,tot){
		ans+=sz[i]*w[i];
	}
	cout<<ans;
}

F

懒标记线段树,两个序列区间加,每次询问∑ai∗bi\sum a_i*b_i∑ai∗bi

一个线段树可以解决,维护a,ba,ba,b两个序列的懒标记,区间和,维护a∗ba*ba∗b的区间和。aaa的懒标记下放时,会对aaa的懒标记区间和产生影响,也会对a∗ba*ba∗b区间和产生影响,bbb的懒标记下放同理。修改需要用两个函数或者参数来表示加的是a,ba,ba,b哪个序列。

c 复制代码
struct Tree
{
#define ls u << 1
#define rs u << 1 | 1
    struct Node
    {
        int l, r;
        ll sa,sb,sum, adda,addb;
    } tr[N << 2];

    void pushup(int u)
    {
        tr[u].sa = tr[ls].sa + tr[rs].sa;
        tr[u].sb = tr[ls].sb + tr[rs].sb;
        tr[u].sum = tr[ls].sum + tr[rs].sum;
        
        tr[u].sa%=M2;
        tr[u].sb%=M2;
        tr[u].sum%=M2;
    }

    void pushdown(int u)
    {
        if (tr[u].adda)
        {
            tr[ls].sa += tr[u].adda * (tr[ls].r - tr[ls].l + 1)%M2;
            tr[rs].sa += tr[u].adda * (tr[rs].r - tr[rs].l + 1)%M2;
            tr[ls].adda += tr[u].adda;
            tr[rs].adda += tr[u].adda;
            
            tr[ls].sum+=tr[u].adda*tr[ls].sb%M2;
            tr[rs].sum+=tr[u].adda*tr[rs].sb%M2;
            tr[ls].sa%=M2;tr[ls].adda%=M2;tr[ls].sum%=M2;
            tr[rs].sa%=M2;tr[rs].adda%=M2;tr[rs].sum%=M2;
            tr[u].adda = 0;
        }
        if (tr[u].addb)
        {
            tr[ls].sb += tr[u].addb * (tr[ls].r - tr[ls].l + 1)%M2;
            tr[rs].sb += tr[u].addb * (tr[rs].r - tr[rs].l + 1)%M2;
            tr[ls].addb += tr[u].addb;
            tr[rs].addb += tr[u].addb;
            
            tr[ls].sum+=tr[u].addb*tr[ls].sa%M2;
            tr[rs].sum+=tr[u].addb*tr[rs].sa%M2;
            tr[ls].sb%=M2;tr[ls].addb%=M2;tr[ls].sum%=M2;
            tr[rs].sb%=M2;tr[rs].addb%=M2;tr[rs].sum%=M2;
            tr[u].addb = 0;
        }
    }

    void build(int u, int l, int r)
    {
        tr[u] = {l, r, 0, 0,0,0,0};
        if (l == r)
        {
            return;
        }
        int mid = (l + r) >> 1;
        build(ls, l, mid);
        build(rs, mid + 1, r);
        pushup(u);
    }

    void modify(int u, int l, int r, int val,int op)
    {
        if (tr[u].l >= l && tr[u].r <= r)
        {
        	if(op==1){
	            tr[u].sa += val * (tr[u].r - tr[u].l + 1)%M2;
	            tr[u].sum+=val*tr[u].sb%M2;
	            tr[u].adda += val;
	            
	            tr[u].sa%=M2;
	            tr[u].sum%=M2;
	            tr[u].adda%=M2;
			}
			else{
	            tr[u].sb += val * (tr[u].r - tr[u].l + 1)%M2;
	            tr[u].sum+=val*tr[u].sa%M2;
	            tr[u].addb += val;		
				
				tr[u].sb%=M2;
				tr[u].sum%=M2;
				tr[u].addb%=M2;	
			}

            return;
        }
        else
        {
            int mid = (tr[u].l + tr[u].r) >> 1;
            pushdown(u);
            if (mid >= l)
                modify(ls, l, r, val,op);
            if (r > mid)
                modify(rs, l, r, val,op);
            pushup(u);
        }
    }

    ll query(int u, int l, int r)
    {
        if (l <= tr[u].l && tr[u].r <= r)
            return tr[u].sum;
        pushdown(u);
        int mid = (tr[u].l + tr[u].r) >> 1;
        if (r <= mid)
            return query(ls, l, r);
        if (l > mid)
            return query(rs, l, r);
        return (query(ls, l, r) + query(rs, l, r))%M2;
    }
} t;
void solve()
{
	int n,q;
	cin>>n>>q;
	t.build(1,1,n);
	rep(i,1,n){
		int x;
		cin>>x;
		t.modify(1,i,i,x,1);
	}
	rep(i,1,n){
		int x;
		cin>>x;
		t.modify(1,i,i,x,2);
	}
	
	rep(i,1,q){
		int op,l,r;
		cin>>op>>l>>r;
		if(op==1||op==2){
			int x;
			cin>>x;
			t.modify(1,l,r,x,op);
		}
		else{
			cout<<t.query(1,l,r)<<'\n';
		}
	}
}
相关推荐
CS创新实验室2 小时前
《计算机网络》深入学:路由算法与路径选择
网络·计算机网络·算法
tod1132 小时前
力扣高频 SQL 50 题阶段总结(四)
开发语言·数据库·sql·算法·leetcode
naruto_lnq2 小时前
C++中的桥接模式
开发语言·c++·算法
苦藤新鸡2 小时前
50.腐烂的橘子
数据结构·算法
无限进步_2 小时前
面试题 02.02. 返回倒数第 k 个节点 - 题解与详细分析
c语言·开发语言·数据结构·git·链表·github·visual studio
想进个大厂2 小时前
代码随想录day32 动态规划01
算法·动态规划
j445566112 小时前
C++中的职责链模式高级应用
开发语言·c++·算法
Hello World . .2 小时前
数据结构:栈和队列
c语言·开发语言·数据结构·vim
uesowys2 小时前
Apache Spark算法开发指导-Decision tree classifier
算法·决策树·spark