zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:Equation(数学+树状数组)

    题目描述

    有一棵$n$个点的以$1$为根的树,以及$n$个整数变量$x_i$。树上$i$的父亲是$f_i$,每条边$(i,f_i)$有一个权值$w_i$,表示一个方程$x_i+x_{f_i}=w_i$,这$n−1$个方程构成了一个方程组。
    现在给出$q$个操作,有两种类型:
    $ullet 1 u v s$,表示询问加上$x_u+x_v=s$这个方程后,整个方程组的解的情况。具体来说,如果方程有唯一解,输出此时$x_1$的值;如果有无限多个解,输出$inf$;如果无解,输出$none$。注意每个询问是独立的。
    $ullet 2 u w$,表示将$w_u$修改为$w$。


    输入格式

    从文件$equation.in$中读入数据。
    第一行两个整数$n,q$。
    接下来$n−1$行,第$i$行有两个整数$f_{i+1}$和$w_{i+1}$。
    接下来$q$行,每行表示一个操作,格式见问题描述。


    输出格式

    输出到文件$equation.out$中。
    对于每个询问输出一行表示答案。


    样例

    样例输入:

    2 7
    1 4
    1 1 2 5
    1 1 2 4
    1 1 1 3
    1 2 2 6
    2 2 3
    1 2 2 10
    1 2 2 -10

    样例输出:

    none
    inf
    none
    1
    -2
    8


    数据范围与提示

    对于所有数据,有$1leqslant n,qleqslant 10^6,1leqslant f_ileqslant i−1,1leqslant u,vleqslant n,−10^3leqslant w,w_ileqslant 10^3,−10^9leqslant sleqslant 10^9$。
    $ullet Subtask1(3\%)$,$nleqslant 10,q=0$。
    $ullet Subtask2(18\%)$,$n=2$。
    $ullet Subtask3(32\%)$,$n,qleqslant 10^3$。
    $ullet Subtask4(33\%)$,$n,qleqslant 10^5$。
    $ullet Subtask5(14\%)$,没有特殊的约束。


    题解

    首先,思考在没有修改的情况下如何快速求出$1$的解,其实无非就是分深度的奇偶,然后算边的前缀和罢了,比方说下图:

    不妨设$4$为已知$x$,那么$3$就是$w_3-x$,$2$就是$w_2-w_3+x$,$1$就是$w_1-w_2+w_3-x$,发现什么规律了没有?

    有点类似于多步容斥的奇加偶减。

    那么考虑如何修改,我们如果要改一条边,那么将会影响到这条边所连两点中儿子的整个子树,显然不能暴力修改。

    发现这个问题其实就是区间修改,单点查询;不妨树状数组?

    利用树状数组维护$DFS$序即可。

    这个题稍卡常……

    听说线段树会$TLE$?

    时间复杂度:$Theta((n+q)log n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec{int nxt,to,w;}e[1000001];
    int head[1000001],cnt;
    int n,q;
    int f[1000001],w[1000001];
    int sum[1000001],depth[1000001],l[1000001],r[1000001],tim;
    int tr[4000001];
    void add(int x,int y,int w)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	e[cnt].w=w;
    	head[x]=cnt;
    }
    void pre_dfs(int x)
    {
    	l[x]=++tim;
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		depth[e[i].to]=depth[x]+1;
    		sum[e[i].to]=w[e[i].to]-sum[x];
    		pre_dfs(e[i].to);
    	}
    	r[x]=tim;
    }
    int lowbit(int x){return x&-x;}
    void change(int l,int r,int w)
    {
    	for(int i=l;i<=n;i+=lowbit(i))tr[i]+=w;
    	for(int i=r+1;i<=n;i+=lowbit(i))tr[i]-=w;
    }
    int ask(int x)
    {
    	int res=0;
    	for(int i=l[x];i;i-=lowbit(i))res+=tr[i];
    	return res*(depth[x]&1?1:-1);
    }
    int main()
    {
    	scanf("%d%d",&n,&q);
    	for(int i=2;i<=n;i++)
    	{
    		scanf("%d%d",&f[i],&w[i]);
    		add(f[i],i,w[i]);
    	}
    	depth[1]=1;
    	pre_dfs(1);
    	while(q--)
    	{
    		int opt;
    		scanf("%d",&opt);
    		if(opt&1)
    		{
    			int u,v,s;
    			scanf("%d%d%d",&u,&v,&s);
    			int flagu=sum[u]+ask(u);
    			int flagv=sum[v]+ask(v);
    			if((depth[u]&1)&&(depth[v]&1))
    			{
    				long long res=1LL*s-flagu-flagv;
    				if(res&1)puts("none");
    				else printf("%lld
    ",res>>1);
    			}
    			else if(!(depth[u]&1)&&!(depth[v]&1))
    			{
    				long long res=1LL*flagu+flagv-s;
    				if(res&1)puts("none");
    				else printf("%lld
    ",res>>1);
    			}
    			else
    			{
    				if(flagu+flagv==s)puts("inf");
    				else puts("none");
    			}
    		}
    		else
    		{
    			int u,v;
    			scanf("%d%d",&u,&v);
    			int delta=v-w[u];
    			w[u]=v;
    			if(!(depth[u]&1))delta=-delta;
    			change(l[u],r[u],delta);
    		}
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    优化 Markdown 在 Notepad++ 中的使用体验
    error C1128: 节数超过对象文件格式限制: 请使用 /bigobj 进行编译
    Git: 代码冲突常见解决方法
    fatal error c1001 编译器中发生内部错误 OpenMesh6.3
    error C2448 函数样式初始值设定项类似函数定义
    VS Code 配置Python
    15分钟掌握 Git
    notepad++快捷键
    【Python笔记】第4章 操作列表
    【MySQL】MySQL按中文排序
  • 原文地址:https://www.cnblogs.com/wzc521/p/11619378.html
Copyright © 2011-2022 走看看