链接:https://ac.nowcoder.com/acm/contest/216/C
来源:牛客网
【题目描述】
众所周知,小K是一只连NOIP2018初赛都没有过的蒟蒻,所以小K很擅长dfs序+分块树,但是本题与dfs序+分块树无关。
小K现在心态爆炸了,因为小K被一道简单的数据结构题给卡住了,希望请你来解决它,但是小K又不想太麻烦你,于是将题面进行了简化(其实是出题人懒得写题面了233333):
Bob有?个点的树,每条边的长度有一个边权,现在定义???(?,?)代表第?个点到第?个点的距离模2之后的结果。问有多少(?,?,?)满足,???(?,?) = ???(?,?) = ???(?,?)。
【输入描述】
第一行一个整数?代表点的数量。
接下来? − 1行每行三个数?,?,?代表有一条在?,?之间长度为?的边。
【输出描述】
一行一个整数代表有多少对(?,?,?)满足条件。
【输入样例】
3
1 2 3
1 3 4
【输出样例】
9
【数据范围】
对于100%的数据,1 ≤ ? ≤ 10000,0 ≤ ? ≤ 233。
思路:原本想到了暴力写部分分:倍增求最近公共祖先+树上差分 ,事实上这种方法不仅不是正解而且效率很低,只能处理不到1000的数据量。
首先分析题目,模2之后的结果只能是0或1,三者相等的情况不可能等于1,可以画出图找找规律,只能等于0,然后通过Dfs找出每个点与根节点的关系,有两种可能,到根节点路径长为偶数,记为0,长为奇数,记为1,将个数分别记录在cnt[0]和cnt[1]中,最后利用乘法原理解决问题。注意,此题不开long long会爆。
下面看了一个牛客网大神的解释:
首先易证得如果i,j,k满足题目中得三个关系式,则他们任意两点间的距离必定为偶数(i,j,k可重复)。
然后可以先用DFS或BFS计算出每个点到根节点得距离(谁是根节点都行,用节点1就不错)。
然后统计出到根节点距离为奇数得点的数量cnt1和到根节点距离为偶数的点的数量cnt2.
自己推一推发现,当且仅当到根节点距离奇偶性相同的点他们之间相互的距离都为偶数。
所以ans=cnt1³+cnt2³.
复杂度O(n)。(转自牛客网)
是不是理解更深了呢?
#include
#include
#include
#define mem(a,b) memset(a,b,sizeof a)
#define LL long long
using namespace std;
const int MAX_N=1e4+7;
int n;
int eid;
struct node{
int v,w,next;
}e[2*MAX_N];
int dist[MAX_N];
int p[MAX_N];
LL cnt[3];
void init()
{
mem(dist,0);
mem(e,0);
mem(p,-1);
eid=0;
}
void insert(int u,int v,int w)
{
e[eid].v=v;
e[eid].w=w;
e[eid].next=p[u];
p[u]=eid++;
}
void insert2(int u,int v,int w)
{
insert(u,v,w);
insert(v,u,w);
}
void dfs(int u,int fa,int w)
{
dist[u]=(dist[fa]+w)%2;
cnt[dist[u]]++;
for(int i=p[u];~i;i=e[i].next)
{
if(e[i].v!=fa)
{
int v=e[i].v;
int w=e[i].w;
dfs(v,u,w);
}
}
}
LL Pow_3(LL x)
{
return x*x*x;
}
int main()
{
scanf("%d",&n);
init();
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
insert2(u,v,w%2);
}
dfs(1,0,0);
printf("%lld",(LL)(Pow_3(cnt[0])+Pow_3(cnt[1])));
return 0;
}