P5588 小猪佩奇爬树
题目描述
佩奇和乔治在爬♂树。
给定 n 个节点的树 T(V,E),第 i 个节点的颜色为 wi,保证有 1≤wi≤n。
对于 1≤i≤n,分别输出有多少对点对 (u,v),满足 u<v,且恰好经过所有颜色为 i 的节点,对于节点颜色不为 i 的其他节点,经过或不经过均可。
树上路径 (u,v) 定义为序列 {f},满足 f1=u,f∣f∣=v,且 ∀1≤i<∣f∣,T 中均存在边 (fi,fi+1),且 {f} 中无重复元素,能够证明对于任意点对 (u,v),其树上路径唯一。
输入格式
第一行 1 个正整数,表示 n 。
第二行 n 个正整数,第 i 个正整数表示 wi。
之后 n−1 行,每行 2 个正整数 u,v,表示 T 中存在边 (u,v)。
输出格式
共 n 行,每行 1 个正整数,第 i 行输出包含所有颜色为 i 的节点的路径个数。
输入输出样例
输入 #1复制
4
1 2 2 3
1 2
2 3
3 4
输出 #1复制
3
4
3
6
输入 #2复制
10
9 7 4 2 3 4 4 5 8 5
2 1
3 2
4 2
5 2
6 4
7 4
8 1
9 4
10 4
输出 #2复制
45
35
9
0
1
45
34
9
17
45
说明/提示

对于第一组样例而言。
对于颜色 1,点对 (1,2),(1,3),(1,4) 满足条件。
对于颜色 2,点对 (1,3),(1,4),(2,3),(2,4) 满足条件。
对于颜色 3,点对 (1,4),(2,4),(3,4) 满足条件。
对于颜色 4,由于图中没有颜色为 4 的节点,所以所有点对均满足条件。
数据范围
对于 40% 的数据,n≤102。
对于 60% 的数据,n≤103。
对于 100% 的数据,n≤106。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
struct q
{
int dep,num;
};
q w[1000010];
int x,y,n[1000010],dep[1000010],l,db[2000010],nxt[2000010],len[2000010];
int f[1000010][21],fath,o,oo,fathe[1000010],ll,h,hh;
long long a,s,sn[1000010],v[1000010],vv;
vector<int> t[1000010];
bool ff;
template <typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flg=0;
for (;!isdigit(c);c=getchar()) if (c=='-') flg=1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
if (flg) x=-x;
}
inline void write(long long x)
{
if (x<0)
{
putchar('-');
x=-x;
}
if (x>=10) write(x/10);
putchar(x%10+48);
}
inline void writeln(long long x)
{
write(x);
puts("");
}
bool cmp(q p,q pp)
{
return p.dep>pp.dep;
}
void wrk(int fa,int u)
{
dep[u]=dep[fa]+1;fathe[u]=fa;
for (int i=0;i<=19;i++)
f[u][i+1]=f[f[u][i]][i];
int k=len[u];
while (k)
{
if (db[k]==fa)
{
k=nxt[k];
continue;
}
f[db[k]][0]=u;
wrk(u,db[k]);
sn[u]+=sn[db[k]];
k=nxt[k];
}
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (int i=20;i>=0;i--)
{
if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
}
for (int i=20;i>=0;i--)
{
if (f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int main(){
read(a);
for (int i=1;i<=a;i++)
{
read(n[i]);
t[n[i]].push_back(i);
}
for (int i=1;i<a;i++)
{
read(x);read(y);
db[i*2-1]=y;
nxt[i*2-1]=len[x];
len[x]=i*2-1;
db[i*2]=x;
nxt[i*2]=len[y];
len[y]=i*2;
}
for (int i=1;i<=a;i++) sn[i]=1;
wrk(0,1);
for (int i=1;i<=a;i++)
{
l=0;
for (int j=0;j<t[i].size();j++)
{
l++;
w[l].num=t[i][j];
w[l].dep=dep[w[l].num];
}
if (l==1)
{
int k=len[w[l].num];ll=0;
while (k)
{
if (db[k]==fathe[w[l].num])
{
k=nxt[k];
continue;
}
ll++;
v[ll]=sn[db[k]];
k=nxt[k];
}
vv=(sn[w[l].num]-1)*(a-sn[w[l].num])+a-1;
for (int j=1;j<=ll;j++)
for (int m=j+1;m<=ll;m++)
vv+=v[j]*v[m];
writeln(vv);
continue;
}
if (l==0)
{
writeln((a*(a-1))/2);
continue;
}
sort(w+1,w+l+1,cmp);
hh=0;
for (int j=2;j<=l;j++)
{
fath=lca(w[1].num,w[j].num);
if (fath!=w[j].num)
{
hh=j;
break;
}
}
ff=0;
if (hh!=0)
for (int j=3;j<=l;j++)
{
o=lca(w[j].num,w[1].num);oo=lca(w[j].num,w[hh].num);
if (((o==w[j].num)||(oo==w[j].num))&&(dep[w[j].num]>=dep[fath])) continue;
ff=1;break;
}
if (ff==0)
{
if (hh==0)
{
int k=w[1].num;h=w[1].num;
while (fathe[k]!=w[l].num)
{
k=fathe[k];
h=k;
}
writeln(sn[w[1].num]*(a-sn[w[l].num]+sn[w[l].num]-sn[h]));
}
else writeln(sn[w[1].num]*sn[w[hh].num]);
}
else writeln(0);
}
return 0;
}
P5536 【XR-3】核心城市
题目描述
X 国有 n 座城市,n−1 条长度为 1 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。
X 国国王决定将 k 座城市钦定为 X 国的核心城市,其余城市为非核心城市。这 k 座核心城市需满足以下两个条件:
- 这 k 座城市可以通过道路,在不经过非核心城市的情况下两两相互到达。
- 定义某个非核心城市与这 k 座核心城市的距离为,这座城市与 k 座核心城市的距离的最小值。
为了衡量交通状况,国王发明了交通拥堵度,它为所有非核心城市与核心城市的距离中的最大值。
问题来了,如何安排核心城市才能使交通拥堵度最小呢?请输出满足条件的最小交通拥堵度。
输入格式
第一行 2 个正整数 n,k。
接下来 n−1 行,每行 2 个正整数 u,v,表示第 u 座城市与第 v 座城市之间有一条长度为 1 的道路。
数据范围:
- 1≤k<n≤105。
- 1≤u,v≤n,u=v,保证城市与道路形成一棵树。
输出格式
一行一个整数,表示满足条件的最小交通拥堵度。
输入输出样例
输入 #1复制
6 3
1 2
2 3
2 4
1 5
5 6
输出 #1复制
1
说明/提示
【样例说明】
钦定 1,2,5 这 3 座城市为核心城市,这样 3,4,6 另外 3 座非核心城市与核心城市的距离均为 1,因此答案为 1。
实现代码:
cpp
#include <bits/stdc++.h>
using namespace std;
struct edge {
int t, nxt;
} e[200010];
struct qwqqwq {
int a, b;
} qwqwqwq[100010];
bool cmp(qwqqwq a, qwqqwq b) {
return a.a > b.a;
}
int head[100010], ep, dis[100010], father[100010], n, k, maxdis[100010], vis[100010], cnt, qqwq[100010];
// queue < int > q;
void add_edge(int s, int t) {
ep++;
e[ep].t = t;
e[ep].nxt = head[s];
head[s] = ep;
}
void dfs1(int now, int fa) {
for(int i = head[now]; i; i = e[i].nxt) {
if(e[i].t == fa) continue;
dis[e[i].t] = dis[now] + 1;
dfs1(e[i].t, now);
}
}
void dfs2(int now, int fa) {
for(int i = head[now]; i ; i = e[i].nxt) {
if(e[i].t == fa) continue;
father[e[i].t] = now;
dis[e[i].t] = dis[now] + 1;
dfs2(e[i].t, now);
}
}
void dfs3(int now, int fa) {
maxdis[now] = dis[now];
for(int i = head[now]; i; i = e[i].nxt) {
if(e[i].t == fa) continue;
dis[e[i].t] = dis[now] + 1;
father[e[i].t] = now;
dfs3(e[i].t, now);
maxdis[now] = max(maxdis[now], maxdis[e[i].t]);
}
}
int main() {
cin >> n >> k;
for(int i = 1; i < n; i++) {
int s, t;
cin >> s >> t;
add_edge(s, t);
add_edge(t, s);
}
dfs1(1, 0);
int qwq = 0, pwp = 1;
for(int i = 1; i <= n; i++) {
if(dis[i] > qwq) {
pwp = i, qwq = dis[i];
}
}
memset(dis, 0, sizeof(dis));
dfs2(pwp, 0);
int qvq = 0; qwq = 1;
for(int i = 1; i <= n; i++) {
if(dis[i] > qvq) {
qwq = i, qvq = dis[i];
}
}
int pos1 = qwq;
for(int i = 1; i <= (dis[qwq] + 1) / 2; i++) {
pos1 = father[pos1];
}
memset(father, 0, sizeof(father));
memset(dis, 0, sizeof(dis));
dfs3(pos1, 0);
// q.push(qwq);
for(int i = 1; i <= n; i++) {
qwqwqwq[i].a = maxdis[i] - dis[i];
//cout << qwqwqwq[i].a << endl;
qwqwqwq[i].b = i;
qqwq[i] = qwqwqwq[i].a;
}
sort(qwqwqwq + 1, qwqwqwq + n + 1, cmp);
for(int i = 1; i <= k; i++) {
vis[qwqwqwq[i].b] = 1;
}
// cout << pos1 << endl;
int ans = 0;
for(int i = 1; i <= n; i++) {
if(!vis[i]) ans = max(qqwq[i] + 1, ans);
}
cout << ans << endl;
return 0;
}
P5836 [USACO19DEC] Milk Visits S
题目描述
Farmer John 计划建造 N 个农场,用 N−1 条道路连接,构成一棵树(也就是说,所有农场之间都互相可以到达,并且没有环)。每个农场有一头奶牛,品种为更赛牛或荷斯坦牛之一。
Farmer John 的 M 个朋友经常前来拜访他。在朋友 i 拜访之时,Farmer John 会与他的朋友沿着从农场 Ai 到农场 Bi 之间的唯一路径行走(可能有 Ai=Bi)。除此之外,他们还可以品尝他们经过的路径上任意一头奶牛的牛奶。由于 Farmer John 的朋友们大多数也是农场主,他们对牛奶有着极强的偏好。他的有些朋友只喝更赛牛的牛奶,其余的只喝荷斯坦牛的牛奶。任何 Farmer John 的朋友只有在他们访问时能喝到他们偏好的牛奶才会高兴。
请求出每个朋友在拜访过后是否会高兴。
输入格式
输入的第一行包含两个整数 N 和 M。
第二行包含一个长为 N 的字符串。如果第 i 个农场中的奶牛是更赛牛,则字符串中第 i 个字符为 G,如果第 i 个农场中的奶牛是荷斯坦牛则为 H。
以下 N−1 行,每行包含两个不同的整数 X 和 Y(1≤X,Y≤N),表示农场 X 与 Y 之间有一条道路。
以下 M 行,每行包含整数 Ai,Bi,以及一个字符 Ci。Ai 和 Bi 表示朋友 i 拜访时行走的路径的端点,Ci 是 G 或 H 之一,表示第 i 个朋友喜欢更赛牛的牛奶或是荷斯坦牛的牛奶。
输出格式
输出一个长为 M 的二进制字符串。如果第 i 个朋友会感到高兴,则字符串的第 i 个字符为 1,否则为 0。
输入输出样例
输入 #1复制
5 5
HHGHG
1 2
2 3
2 4
1 5
1 4 H
1 4 G
1 3 G
1 3 H
5 5 H
输出 #1复制
10110
说明/提示
在这里,从农场 1 到农场 4 的路径包括农场 1、2 和 4。所有这些农场里都是荷斯坦牛,所以第一个朋友会感到满意,而第二个朋友不会。
关于部分分:
测试点 1 样例。
测试点 2∼5 满足 N≤103,M≤2⋅103。
对于 100% 的数据,1≤N≤105,1≤M≤105。
供题:Spencer Compton
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
int fa[100010],ans[100010],M,N;
char col[100010];
int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
fa[find(x)]=find(y);
}
int main()
{
int cnt=0;
cin>>N>>M;
for(int i=1;i<=N;i++)
{
fa[i]=i;
cin>>col[i];
}
for(int i=1;i<=N-1;i++)
{
int u,v;cin>>u>>v;
if(col[u]==col[v]) merge(u,v);
}
for(int i=1;i<=M;i++)
{
int a,b; cin>>a>>b;
char c; cin>>c;
if(find(a)==find(b)&&col[a]!=c) ans[++cnt]=0;
else ans[++cnt]=1;
}
for(int i=1;i<=cnt;i++) cout<<ans[i];
return 0;
}