暑假考试题6:single 单(树上推理)

题目:

分析:

对于t=0的点,显然暴力会重复计算很多。设dis为一个点以下,所有儿子到它的val*dis和,sum为子树权值和,利用父亲已知的b与计算出的dis和sum来计算儿子的值。(必须要用bfs 保证更新的顺序)

(当然我是做麻烦了的)

而t=1,要求知道b反推a,那么对一个点的b值进行分析,联立列方程去解a。

这里用到了一个重要的性质:根的b为∑i=2~n sum[i] (仔细想想可知)

然后一下就是关于如何解a的过程:

#include<bits/stdc++.h>
using namespace std;
#define N 100005
#define ll long long
int to[N<<1],nex[N<<1],tot=0,head[N],fa[N],vis[N];
ll sumer=0,sum[N],dis[N],a[N],b[N],total=0;
ll read()
{
    ll x=0; int fl=1; char ch=getchar();
    while(ch>‘9‘||ch<‘0‘) { if(ch==‘-‘) fl=-1; ch=getchar(); }
    while(ch<=‘9‘&&ch>=‘0‘) { x=x*10+ch-‘0‘; ch=getchar(); }
    return x*fl;
}
void add(int a,int b) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; }
void dfs1(int u)
{
    sum[u]=a[u];
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        if(v==fa[u]) continue;
        fa[v]=u;
        dfs1(v);
        sum[u]+=sum[v];
        dis[u]+=dis[v]+sum[v];
    }
}
void bfs()
{
    queue<int> q; memset(vis,0,sizeof(vis));
    q.push(1);
    for(int i=head[1];i;i=nex[i]){
        int v=to[i];
        b[1]+=dis[v]+sum[v];
    }
    while(!q.empty()){
        int u=q.front(); q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=nex[i]){
            int v=to[i]; if(v==fa[u]) continue;
            b[v]=dis[v]+b[u]-(dis[v]+sum[v])+(sum[1]-sum[v]);
            q.push(v);
        }
    }
}
void dfs2(int u)
{
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        if(v==fa[u]) continue;
        fa[v]=u;
        sumer+=b[u]-b[v];
        dfs2(v);
    }
}
void dfs3(int u)
{
    ll tmp=0;
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        if(v==fa[u]) continue;
        sum[v]=(b[u]-b[v]+total)/2;
        tmp+=sum[v];
        dfs3(v);
    }
    a[u]=sum[u]-tmp;
}
void clear(int n)
{
    tot=0;
    for(int i=1;i<=n*2;i++)
    head[i]=0,sum[i]=0,dis[i]=0,nex[i]=0,to[i]=0,b[i]=0,a[i]=0;
}
int main()
{
    freopen("single.in","r",stdin);
    freopen("single.out","w",stdout);
    int T,aa,bb,t,n=0;
    T=read();
    while(T--){
        clear(n);
        n=read();
        for(int i=1;i<=n-1;i++){
            aa=read(); bb=read();
            add(aa,bb); add(bb,aa);
        }
        t=read();
        if(t==0){
            for(int i=1;i<=n;++i) a[i]=read();
            dfs1(1); bfs();
            for(int i=1;i<=n;++i) printf("%lld ",b[i]);
            printf("\n");
        }
        else{
            sumer=0;
            for(int i=1;i<=n;++i) b[i]=read();
            dfs2(1);//printf("%lld\n",sumer);
            total=(2*b[1]-sumer)/(n-1);
            sum[1]=total;//printf("%lld\n",total);
            dfs3(1);//
            for(int i=1;i<=n;++i) printf("%lld ",a[i]);
            printf("\n");
        }
    }
}
/*
100
8
1 2
1 3
2 4
2 5
2 6
4 7
4 8
1
16 17 21 28 30 30
0
3 3 5 2 1 1
1
7000002008 6000001005 10000003011 9000000004 9000002008 11000002006 12000001007
14000001003
0
1000000000 1000000000 1000000000 1000 1000000000 1 1000000000 2
6
1 2
1 3
2 4
2 5
2 6
1
13 10 22 21 21 21

0
3 5 2 1 1 1 3 4

0
3 5 2 1 1 1
*/

原文地址:https://www.cnblogs.com/mowanying/p/11425642.html

时间: 2024-10-10 10:23:55

暑假考试题6:single 单(树上推理)的相关文章

暑假考试题2:Nim游戏 改(博弈论)

题目: 其实就是在nim游戏基础上添加了一次可以不取的机会. 多堆石子可以看成多个游戏,它们起点的sg值异或起来就是整个游戏的sg值,若sg值为1,则先手必胜,为0,则后手必胜. 关键在于怎么求sg值:可以打表找规律->对游戏局面进行动态dfs连边,再dfs一遍求sg值(也就是求mex值) 细节:dfs能跑到的范围很小,最多到20(可能还达不到,因为边实在是太多了),所以死循环时不要怀疑是自己打错了,还可能是石子数太大了. 规律:石子数为奇数,mex值为a[i]+1,偶数,mex值为a[i]-1

暑假考试题3:baritone 上低音号与星星(链表+矩形统计)

题目: n,r,c<=3000 分析:先枚举左边界   然后将点从高到矮连链表   再统计从每一个点开始含括k个点的矩形   能够上下延伸得到的多少个矩形.然后枚举右边界删点    利用大矩形原有的信息修改后    去累加新的左右宽度较小的矩形的贡献. 这种算法的优势: 每一次缩小矩形的时候 可以不用重新计算小矩形的贡献 只需要通过大矩形原有的贡献进行修改 修改方式:通过枚举右边界缩小矩形 删掉超出右边界范围内的那一列的点删点时重新统计贡献 :也就是对被影响的点重新计算一下(通过跳链表找到受影响

暑假考试题4:星际旅行(欧拉路)

题目: 分析: 题目大意:从任意点出发,任意点结束,在经过所有边的情况下选择两条边只经过一次,其它都经过两次. 先不考虑自环:这道题看起来很像欧拉路,但欧拉路是每条边只经过一次,那么我们考虑:把边数翻倍,选择两条边删去,使得剩下的是一个欧拉路. 边数翻倍后,每一个点的度数都是偶数 欧拉路的判定:只有两个点是奇数点,其它都是偶数点(奇偶指度数)证明 找不同的两条边删去,再考虑自环的因素,就应该分类讨论: 1.删两条边:通过画图推出,删的两条边一定是连在同一个点上,因为只有这样才会出现两个奇数点.

设计模式--单例

单例设计模式用来创建唯一的对象,有些时候我们只需要一个对象,如:线程池,缓存,对话框,注册表,日志对象,等等.这就需要单例设计模式来完成.不用多说,直接上代码.public class TestSingle { private static TestSingle single = null; private TestSingle(){} public static TestSingle getInstance(){ if(single==null){ single = new TestSingl

基于JQ的单双日历,本人自己写的哈,还没封装,但是也能用

<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>小日历</title> </head> <style rel="stylesheet" type="text/css"> *{ padding:0; margin:0; } /*---日历样式---*/ .mycalendar{ wid

世界上所有值得收藏的书单(持续更新)

1.这20本书,是8万网友力荐的"前半部分超无聊,看完发现是神作"的书 2.这20本书,是8万网友力荐的通宵都要读完的书 3.这20本书,是6万网友力荐的看完就泪崩的书 4.这30本书,是5万网友自荐的“给五星都不够”的书 5.这30本书,是5万网友自荐的“颠覆你世界观”的书 6.20世纪最优秀的100部中文小说 7.股神巴菲特认为每个人都该读的8本书 8.有史以来最伟大的100部非虚构图书 9.联想创始人柳传志推荐的8本书 10.村上春树推荐的20本书 11.BBC评选:21世纪最伟

【linux基础】19、系统初始化流程

一.内核 linux系统的组成:内核(kernel)+根文件系统(rootfs) 1.内核的功能 进程管理:task_struct,scheduler(调度) 内存管理: I/O管理:中断及中断处理 文件系统: 驱动程序 安全相关功能:SElinux,各种加密库 2.内核设计流派 单内核:单一体系 将所有功能都作成一个整体,都作在内核中 linux: 模块化设计:核心 + 外围功能性模块组成 内核支持动态装卸载模块  .ko文件:kernel object 微内核:内核子系统 windows,s

Spring学习3—控制反转(IOC)Spring依赖注入(DI)和控制反转(IOC)

一.思想理解 Spring 能有效地组织J2EE应用各层的对象.不管是控制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调.运行.Spring将各层的对象以松耦合的方式组织在一起,Action对象无须关心Service对象的具体实现,Service对 象无须关心持久层对象的具体实现,各层对象的调用完全面向接口.当系统需要重构时,代码的改写量将大大减少. 上面所说的一切都得宜于Spring的核心机制,依赖注入.依赖注入让bean与

Linux的运行级别

一.Linux的运行级别 1.查看当前运行级别的命令:runlevel     2.每个运行级别的作用 二.各运行级别下的含义 1.init 0(关机) # ls /etc/rc.d/rc0.d K05wdaemon             K60crond               K84NetworkManager        K90network     K10saslauthd             K73winbind            K84wpa_supplicant