Acwing 252. 树
题意:
给定一个有 N 个点(编号 0,1,…,N−1)的树,每条边都有一个权值(不超过 1000)。
树上两个节点 x 与 y 之间的路径长度就是路径上各条边的权值之和。
求长度不超过 K 的路径有多少条。
1≤N≤104,
1≤K≤5×106,
0≤l≤103
题解:
P4178 Tree,和这个题题意是一样的,但是本题的数据范围更大,本题k<=5e6,且空间只给了10MB,树状数组的方法是不行,需要用容斥
我们cal直接记录以u为出发点的所有路径长度,用数组d来存,对数组d排序,然后利用尺取的方法来确定有多少对长度之和小于k。但是这样是会造成重复的
对于图中绿色两点,其路径应该是紫色线,但是我们在计算u时会将红色部分也计算进去,因此我们要将多余部分删去,对于u的儿子节点v,我们计算一遍以v为出发点的路径(还要额外加上边长dis(u,v) ),有多少对长度之和小于k,相当于绿线部分,然后减去绿线部分,相当于将多余部分删去
代码:
#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
void read(){};
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{x= 0;char c= getchar();bool flag= 0;while (c < '0' || c > '9')flag|= (c == '-'), c= getchar();while (c >= '0' && c <= '9')x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();if (flag)x= -x;read(Ar...);
}
template <typename T> inline void write(T x)
{if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef LOCALstartTime= clock();freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef LOCALendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn= 1e5 + 9;
int n, k;
vector<PII> vec[maxn];
int vis[maxn];
int rt, cnt;
int sz[maxn], d[maxn];
void dfs_rt(int u, int fa, int tot)
{sz[u]= 1;int maxx= 0;for (auto it : vec[u]) {int v= it.first;int w= it.second;if (v == fa || vis[v])continue;dfs_rt(v, u, tot);sz[u]+= sz[v];maxx= max(maxx, sz[v]);}maxx= max(maxx, tot - sz[u]);if (2 * maxx <= tot)rt= u;
}
void dfs_dis(int u, int fa, int dis)
{d[++cnt]= dis; //记录了所有路径长度for (auto it : vec[u]) {int v= it.first;int w= it.second;if (v == fa || vis[v])continue;dfs_dis(v, u, dis + w);}
}
int calc(int u, int w)
{cnt= 0;dfs_dis(u, 0, w);sort(d + 1, d + 1 + cnt);int l= 1, r= cnt, ans= 0;while (l < r) {while (d[l] + d[r] > k && l < r)r--;/*d是递增的,当d[l]+d[r]<=k时,此后所有r到l这段都是小于k的*/ans+= (r - l);l++;}return ans;
}
int solve(int u, int fa, int tot)
{dfs_rt(u, fa, tot);u= rt;vis[u]= 1;int res= calc(u, 0);for (auto it : vec[u]) {int v= it.first;int w= it.second;if (vis[v])continue;res-= calc(v, w);res+= solve(v, u, cnt);}return res;
}
int main()
{//rd_test();while (1) {read(n, k);if (!n && !k)break;for (int i= 1; i <= n; i++)vec[i].clear();for (int i= 1; i <= n; i++)vis[i]= 0;int u, v, w;for (int i= 1; i < n; i++) {read(u, v, w);u++;v++;vec[u].push_back({v, w});vec[v].push_back({u, w});}printf("%d\n", solve(1, 0, n));}return 0;//Time_test();
}