“华为杯”杭州电子科技大学2023新生编程大赛---树
Problem Description
给定一棵包含?n?个节点的带边权的树,树是一个无环的无向联通图。定义?xordist(u,v)?为节点?u?到?v?的简单路径上所有边权值的异或和。
有?q?次询问,每次给出 `l r x`,求?∑ri=lxordist(i,x)?的值。
Input
测试点包含多组数据。第一行包含一个整数?T(1≤T≤10),表示数据组数。每组数据的输入格式如下:
第一行包含一个整数?n(1≤n≤105),表示节点的个数。
接下来?n?1?行,每行包含三个整数?u、v?和?w(1≤u,v≤n,0≤w<230),表示?u?和?v?之间存在一条权值为?w?的无向边。保证输入是一棵树。
接下来一行,包含一个整数?q(1≤q≤105),表示询问的次数。
接下来?q?行,每行包含三个整数?l、r?和?x(1≤l≤r≤n,1≤x≤n),分别表示每次询问的信息,其含义已在上文说明。
Output
每组数据包含?q?行,每行一个整数,表示每次询问的答案。
题意:
定义了一个函数? xordist(u,v)?为节点?u?到?v?的简单路径上所有边权值的异或和。
给你多次询问,求有?q?次询问,每次给出 l, r, x,求??xordist(i,x)?的值。
思路:
首先可以知道,我们任意选一点为根 root 往下递归异或就可以得到 f [ i ](root 到 i 的路径异或值 ),那么? l 到 r 的路劲异或值可以由 f [ l ] ^ f [ r ]得出;
那么如何计算答案呢,就是用 f [ l ]~f [ r ] 分别异或f [ x ] 相加即可,但是1e5级别的询问显然时间复杂度不可以接受,然后我们就行有什么可以快速算出 l ~ r? 的贡献呢,这时候就看思维发不发散了,这里可以想到用前缀和;
(当然不是异或前缀和,异或不满足分配律,比如 (2^3+2^3+4^3)!=8^3
所以是另一种 :计算1~n , f [ i ] 2进制的每一位1和0的前缀和,
那么答案就是,对f [ x ] 的每一位的贡献计算,比如f [ x ] 第2位是0,那么根据异或1异或0才有贡献, 贡献就是 pow( 2 , i (第几位) )*( sum1[ r ][ i ]-sum1[ l-1 ][ i ] );
复杂度位1e5*30,显然可以接受
完毕
int n;
int f[N];
vector<PII> g[N];
void dfs(int u, int fa)
{
for (auto ed : g[u])
{
if (ed.xx == fa)
continue;
f[ed.xx] = f[u] ^ ed.yy;
dfs(ed.xx, u);
}
}
int qpow(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1)
res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
g[i].clear();
f[i] = 0;
}
int root = inf;
for (int i = 1; i <= n - 1; i++)
{
int a, b, c;
cin >> a >> b >> c;
g[a].pb({b, c});
g[b].pb({a, c});
root = min({a, b, root});
}
dfs(root, -1);
vector<vector<int>> sum1(n + 2, vector<int>(32));
vector<vector<int>> sum0(n + 2, vector<int>(32));
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= 29; j++)
{
int x = (f[i] >> j & 1);
if (x)
sum1[i][j]++;
else
sum0[i][j]++;
sum1[i][j] += sum1[i - 1][j];
sum0[i][j] += sum0[i - 1][j];
}
}
int q;
cin >> q;
while (q--)
{
int l, r, x;
cin >> l >> r >> x;
int ans = 0;
for (int i = 0; i <= 29; i++)
{
int t = (f[x] >> i & 1);
if (t)
ans += qpow(2, i) * (sum0[r][i] - sum0[l - 1][i]);
else
ans += qpow(2, i) * (sum1[r][i] - sum1[l - 1][i]);
}
cout << ans << endl;
}
}
signed main()
{
Yshanqian;
int T;
T = 1;
cin >> T;
for (int cases = 1; cases <= T; ++cases)
{
// cout<<"Case #"<<cases<<": ";
solve();
}
return 0;
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!