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 的父母会遵守以下两条规则:
- 如果 A 距离 C 比 B 距离 C 近,那么 Chris 的父母先去 Shermie 家寻找 Chris,如果找不到,Chris 的父母再去 Yashiro 家;反之亦然。
- 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);
}