洛谷-【图论2-1】树5

P1351 [NOIP 2014 提高组] 联合权值

题目背景

NOIP2014 提高组 D1T2

题目描述

无向连通图 G 有 n 个点,n−1 条边。点从 1 到 n 依次编号,编号为 i 的点的权值为 Wi​,每条边的长度均为 1。图上两点 (u,v) 的距离定义为 u 点到 v 点的最短距离。对于图 G 上的点对 (u,v),若它们的距离为 2,则它们之间会产生 Wv​×Wu​ 的联合权值。

请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?

输入格式

第一行包含 1 个整数 n。

接下来 n−1 行,每行包含 2 个用空格隔开的正整数 u,v,表示编号为 u 和编号为 v 的点之间有边相连。

最后 1 行,包含 n 个正整数,每两个正整数之间用一个空格隔开,其中第 i 个整数表示图 G 上编号为 i 的点的权值为 Wi​。

输出格式

输出共 1 行,包含 2 个整数,之间用一个空格隔开,依次为图 G 上联合权值的最大值和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对 10007 取余。

输入输出样例

输入 #1复制

复制代码
5  
1 2  
2 3
3 4  
4 5  
1 5 2 3 10 

输出 #1复制

复制代码
20 74

说明/提示

样例解释

本例输入的图如上所示,距离为 2 的有序点对有(1,3) 、(2,4) 、(3,1) 、(3,5)、(4,2) 、(5,3)。

其联合权值分别为 2,15,2,20,15,20。其中最大的是 20,总和为 74。

数据说明

  • 对于 30% 的数据,2<n≤100;
  • 对于 60% 的数据,2<n≤2000;
  • 对于 100% 的数据,2<n≤2×105,0<Wi≤10000。

保证一定存在可产生联合权值的有序点对。

实现代码:

cpp 复制代码
#include<cstdio>
using namespace std;
struct edge
{
    int next;
    int to;
}a[400005];
int edgenum,head[200005],w[200005];
int n,ans,maxx;
void add(int u,int v)
{
    a[++edgenum].next=head[u];
    a[edgenum].to=v;
    head[u]=edgenum;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    for(int i=1;i<=n;i++)
      scanf("%d",&w[i]);
    for(int i=1;i<=n;i++)
    {
        int max1=0,max2=0;
        int t1=0,t2=0;
        for(int j=head[i];j;j=a[j].next)
        {
            if(w[a[j].to]>max1)max2=max1,max1=w[a[j].to];
            else if(w[a[j].to]>max2)max2=w[a[j].to];
            t1=(t1+w[a[j].to])%10007;
            t2=(t2+w[a[j].to]*w[a[j].to])%10007;
        }
        t1=t1*t1%10007;
        ans=(ans+t1+10007-t2)%10007;
        if(maxx<max1*max2)maxx=max1*max2;
    }
    printf("%d %d\n",maxx,ans);
    return 0;
}

P4408 [NOI2003] 逃学的小孩 / 数据生成器

题目描述

Chris 家的电话铃响起了,里面传出了 Chris 的老师焦急的声音:"喂,是 Chris 的家长吗?你们的孩子又没来上课,不想参加考试了吗?"一听说要考试,Chris 的父母就心急如焚,他们决定在尽量短的时间内找到 Chris。他们告诉 Chris 的老师:"根据以往的经验,Chris 现在必然躲在朋友 Shermie 或 Yashiro 家里偷玩《拳皇》游戏。现在,我们就从家出发去找 Chris,一旦找到,我们立刻给您打电话。"说完砰的一声把电话挂了。

Chris 居住的城市由 N 个居住点和若干条连接居住点的双向街道组成,经过街道 x 需花费 Tx​ 分钟。可以保证,任意两个居住点间有且仅有一条通路。Chris 家在点 C,Shermie 和 Yashiro 分别住在点 A 和点 B。Chris 的老师和 Chris 的父母都有城市地图,但 Chris 的父母知道点 A、B、C 的具体位置而 Chris 的老师不知。

为了尽快找到 Chris,Chris 的父母会遵守以下两条规则:

  1. 如果 A 距离 C 比 B 距离 C 近,那么 Chris 的父母先去 Shermie 家寻找 Chris,如果找不到,Chris 的父母再去 Yashiro 家;反之亦然。
  2. Chris 的父母总沿着两点间唯一的通路行走。

显然,Chris 的老师知道 Chris 的父母在寻找 Chris 的过程中会遵守以上两条规则,但由于他并不知道 A、B、C 的具体位置,所以现在他希望你告诉他,最坏情况下 Chris的父母要耗费多长时间才能找到 Chris?

输入格式

输入文件第一行是两个整数 N 和 M,分别表示居住点总数和街道总数。

以下 M 行,每行给出一条街道的信息。第 i+1 行包含整数 Ui​、Vi​、Ti​,表示街道 i 连接居住点 Ui​ 和 Vi​,并且经过街道 i 需花费 Ti​ 分钟。街道信息不会重复给出。

输出格式

输出文件仅包含整数 T,即最坏情况下 Chris 的父母需要花费 T 分钟才能找到 Chris。

输入输出样例

输入 #1复制

复制代码
4 3
1 2 1
2 3 1
3 4 1

输出 #1复制

复制代码
4

说明/提示

对于 100% 的数据,3≤N≤2×105,1≤Ui​,Vi​≤N,0≤Ti​≤109。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=200010;
struct edge{
    int to,next,w;
}e[maxn<<1];
int n,m,cnt;
int dis1[maxn],dis2[maxn],last[maxn];
int st,ed;
inline void add(int u,int v,int w){
    cnt++;
    e[cnt].to=v;
    e[cnt].next=last[u];
    last[u]=cnt;
    e[cnt].w=w;
}
void dfs1(int u,int fa){
    for(int i=last[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa) continue;
        dis1[v]=dis1[u]+e[i].w;
        if(dis1[v]>dis1[st]) st=v;
        dfs1(v,u);
    }
}
void dfs2(int u,int fa){
    for(int i=last[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa) continue;
        dis2[v]=dis2[u]+e[i].w;
        if(dis2[v]>dis2[ed]) ed=v;
        dfs2(v,u);
    }
}
void dfs3(int u,int fa){
    for(int i=last[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa) continue;
        dis1[v]=dis1[u]+e[i].w;
        dfs3(v,u);
    }
}
void dfs4(int u,int fa){
    for(int i=last[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa) continue;
        dis2[v]=dis2[u]+e[i].w;
        dfs4(v,u);
    }
}
signed main(){
    cin>>n>>m;
    for(int i=1,u,v,w;i<=m;i++){
        scanf("%lld %lld %lld",&u,&v,&w);
        add(u,v,w),add(v,u,w);
    }
    dfs1(1,0);
    dfs2(st,0);
    int ans=dis2[ed];
    memset(dis1,0,sizeof(dis1)),memset(dis2,0,sizeof(dis2));
    dfs3(st,0),dfs4(ed,0);
    int tmp=0;
    for(int i=1;i<=n;i++){
        int d=min(dis1[i],dis2[i]);
        if(d>tmp) tmp=d;
    }
    ans+=tmp;
    cout<<ans;
}

P3398 仓鼠找 sugar

题目描述

小仓鼠的和他的基(mei)友(zi)sugar 住在地下洞穴中,每个节点的编号为 1∼n。地下洞穴是一个树形结构。这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c)到图书馆(d)。他们都会走最短路径。现在小仓鼠希望知道,有没有可能在某个地方,可以碰到他的基友?

小仓鼠那么弱,还要天天被 zzq 大爷虐,请你快来救救他吧!

输入格式

第一行两个正整数 n 和 q,表示这棵树节点的个数和询问的个数。

接下来 n−1 行,每行两个正整数 u 和 v,表示节点 u 到节点 v 之间有一条边。

接下来 q 行,每行四个正整数 a、b、c 和 d,表示节点编号,也就是一次询问,其意义如上。

输出格式

对于每个询问,如果有公共点,输出大写字母 Y;否则输出N

输入输出样例

输入 #1复制

复制代码
5 5
2 5
4 2
1 3
1 4
5 1 5 1
2 2 1 4
4 1 3 4
3 1 1 5
3 5 1 4

输出 #1复制

复制代码
Y
N
Y
Y
Y

说明/提示

本题时限 1s,内存限制 128M,因新评测机速度较为接近 NOIP 评测机速度,请注意常数问题带来的影响。

20% 的数据 n,q≤200。

40% 的数据 n,q≤2×103。

70% 的数据 n,q≤5×104。

100% 的数据 1≤n,q≤105。

实现代码:

cpp 复制代码
#include <cstdio>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define rg register
#define ci const int
#define cl const long long

typedef long long int ll;

namespace IPT {
	const int L = 1000000;
	char buf[L], *front=buf, *end=buf;
	char GetChar() {
		if (front == end) {
			end = buf + fread(front = buf, 1, L, stdin);
			if (front == end) return -1;
		}
		return *(front++);
	}
}

template <typename T>
inline void qr(T &x) {
	rg char ch = IPT::GetChar(), lst = ' ';
	while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
	while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
	if (lst == '-') x = -x;
}

template <typename T>
inline void ReadDb(T &x) {
	rg char ch = IPT::GetChar(), lst = ' ';
	while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
	while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
	if (ch == '.') {
		ch = IPT::GetChar();
		double base = 1;
		while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
	}
	if (lst == '-') x = -x;
}

namespace OPT {
	char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
	if (x < 0) {x = -x, putchar('-');}
	rg int top=0;
	do {OPT::buf[++top] = x % 10 + '0';} while (x /= 10);
	while (top) putchar(OPT::buf[top--]);
	if (pt) putchar(aft);
}

const int maxn = 100010;
const int maxm = 200010;

struct Edge {
	int to, nxt;
};
Edge edge[maxm]; int hd[maxn], ecnt = 1;
inline void cont(ci from, ci to) {
	Edge &e = edge[++ecnt];
	e.to = to; e.nxt = hd[from]; hd[from] = ecnt;
}

int n, q, vistime;
int sz[maxn], son[maxn], dfn[maxn], top[maxn], deepth[maxn], fa[maxn];

struct Tree {
	Tree *ls, *rs;
	int l, r, v, tg;
	
	inline void make_tag(ci _v) {
		this->v += _v;
		this->tg += _v;
	}
	
	inline void pushdown() {
		if (!this->tg) return;
		if (this->ls) this->ls->make_tag(tg);
		if (this->rs) this->rs->make_tag(tg);
		this->tg = 0;
	}
	
	inline void pushup() {
		this->v = this->ls ? (this->rs ? std::max(this->ls->v, this->rs->v) : this->ls->v) : this->rs->v;
	}
};
Tree *pool[maxm], qwq[maxm], *rot;
int Top;

void reading();
void dfs(ci, ci);
void DFS(ci, ci);
void buildpool();
void buildroot();
void build(Tree*, ci, ci);
void change(int, int, ci);
void update(Tree*, ci, ci, ci);
bool check(int, int);
int ask(Tree*, ci, ci);

int main() {
	freopen("1.in", "r", stdin);
	qr(n); qr(q);
	reading();
	dfs(1,0); DFS(1, 1);
	buildpool(); buildroot();
	build(rot, 1, n);
	int a, b, c, d;
	while(q--) {
		a = b = c = d = 0;
		qr(a); qr(b); qr(c); qr(d);
		change(a, b, 1);
		if(check(c, d)) puts("Y");
		else puts("N");
		change(a, b, -1);
	}
	return 0;
}

void reading() {
	int a, b;
	for (rg int i = 1; i < n; ++i) {
		a = b = 0; qr(a); qr(b);
		cont(a, b); cont(b, a);
	}
}

void dfs(ci u, ci pree) {
	deepth[u] = deepth[fa[u] = edge[pree].to] + 1;
	sz[u] = 1;
	for (int i = hd[u]; i; i = edge[i].nxt) if (i != pree) {
		int &to = edge[i].to;
		dfs(to, i ^ 1);
		sz[u] += sz[to];
		if(sz[to] > sz[son[u]]) son[u] = to;
	}
}

void DFS(ci u, ci tp) {
	if ((!u) || (dfn[u])) return;
	dfn[u] = ++vistime; top[u] = tp;
	DFS(son[u], tp);
	for (int i = hd[u]; i; i = edge[i].nxt) {
		int &to = edge[i].to;
		DFS(to, to);
	}
}

void buildpool() {
	for (rg int i = 0; i < maxm; ++i) pool[i] = qwq + i;
	Top = maxm - 1;
}

void buildroot() {
	rot = pool[Top--];
}

void build(Tree *u, ci l, ci r) {
	u->l = l; u->r = r;
	if(l == r) return;
	int mid = (l + r) >> 1;
	if (l <= mid) build(u->ls = pool[Top--], l, mid);
	if (mid < r) build(u->rs = pool[Top--], mid + 1, r);
}

void change(int u, int v, ci p) {
	while (top[u] != top[v]) {
		if (deepth[top[u]] < deepth[top[v]]) std::swap(u, v);
		update(rot, dfn[top[u]], dfn[u], p);
		u = fa[top[u]];
	}
	if (deepth[u] < deepth[v]) std::swap(u, v);
	update(rot, dfn[v], dfn[u], p);
}

void update(Tree *u, ci l, ci r, ci v) {
	if ((u->l > r) || (u->r < l)) return;
	if ((u->l >= l) && (u->r <= r)) {u->make_tag(v);return;}
	u->pushdown();
	if (u->ls) update(u->ls, l, r, v);
	if (u->rs) update(u->rs, l, r, v);
	u->pushup();
}

bool check(int u, int v) {
	while (top[u] != top[v]) {
		if (deepth[top[u]] < deepth[top[v]]) std::swap(u, v);
		if (ask(rot, dfn[top[u]], dfn[u])) return true;
		u = fa[top[u]];
	}
	if (deepth[u] < deepth[v]) std::swap(u, v);
	return ask(rot, dfn[v], dfn[u]);
}

int ask(Tree *u, ci l, ci r) {
	if ((u->l > r) || (u->r < l)) return 0;
	if ((u->l >= l) && (u->r <= r)) return u->v;
	u->pushdown();
	return u->ls ? (u->rs ? std::max(ask(u->ls, l, r), ask(u->rs, l ,r)) : ask(u->ls, l, r)) : ask(u->rs, l, r);
}
相关推荐
threelab1 小时前
Three.js 初中数学函数可视化 | 三维可视化 / AI 提示词
开发语言·前端·javascript·人工智能·3d·着色器
咖啡里的茶i2 小时前
视觉显著目标的自适应分割与动态网格生成算法研究
人工智能·算法·目标跟踪
xiaoshuaishuai82 小时前
C# CDN加速与离线包优化PowerSetting慢问题
开发语言·windows·spring·c#
paeamecium2 小时前
【PAT甲级真题】- String Subtraction (20)
数据结构·c++·算法·pat考试·pat
凉辰2 小时前
解决 H5 键盘遮挡与页面上推
开发语言·javascript·计算机外设
YL200404262 小时前
047从前序与中序遍历序列构造二叉树
算法·leetcode
极梦网络无忧2 小时前
password_hash
算法·哈希算法
计算机安禾3 小时前
【c++面向对象编程】第25篇:仿函数(函数对象):重载operator()
开发语言·c++·算法
Rust语言中文社区3 小时前
【Rust日报】2026-05-14 Pyrefly v1.0 正式发布:快速的 Python 类型检查器和语言服务器
开发语言·后端·python·rust