蒟蒻来讲题,还望大家喜。若哪有问题,大家尽可提!
Hello, 大家好哇!本初中生蒟蒻用Dijkstra的堆优化版来解决洛谷P8965 坠梦 | Falling into Dream这一道题!
===========================================================================================
坠梦 | Falling into Dream
题目背景
神明愚弄凡间,所谓命运,不过是神明掷出的一颗骰子而已。
花朵等不到的蝴蝶,终究成了一分蹊跷的梦,一轮轮再次重启。
神明的提线木偶一次又一次的被扼住脖颈, 以爱的名义,消逝在时间的花海里。
无数的执念背后,都有一个被扭曲的“真理”。
你所承诺的没有出现,彻夜无眠,或许我只是自作主张的,替你爱了一次人间
“最虔诚者只祝祷,不虔诚者才有所求。”
没有过信仰,因为舍命救了一个人,有幸来到了天堂。
题目描述
给定一棵 n n n 个结点的无根树,每条边有非负整数边权。结点由 1 ∼ n 1 \sim n 1∼n 编号。
对于每一个点对 ( x , y ) (x, y) (x,y),定义 ( x , y ) (x, y) (x,y) 的距离 dis ( x , y ) \operatorname{dis}(x, y) dis(x,y) 为 x , y x,y x,y 两点之间唯一简单路径上边权的异或和。
给定两个结点 x , y x, y x,y,定义点 i i i 的价值 val x , y ( i ) \operatorname{val}_{x, y}(i) valx,y(i) 为 ( x , i ) (x, i) (x,i) 与 ( y , i ) (y, i) (y,i) 的距离的异或和,即
val x , y ( i ) = dis ( x , i ) ⊕ dis ( y , i ) 。 \operatorname{val}_{x, y}(i) = \operatorname{dis}(x, i) \oplus \operatorname{dis}(y, i) \textsf{。} valx,y(i)=dis(x,i)⊕dis(y,i)。
现在有 q q q 次询问,每次询问给出四个整数 x , y , l , r x, y, l, r x,y,l,r,求 ⨁ i = l r val x , y ( i ) \displaystyle \bigoplus_{i = l}^{r} \operatorname{val}_{x, y}(i) i=l⨁rvalx,y(i) 的值,即求
val x , y ( l ) ⊕ val x , y ( l + 1 ) ⊕ ⋯ ⊕ val x , y ( r − 1 ) ⊕ val x , y ( r ) 。 \operatorname{val}_{x, y}(l) \oplus \operatorname{val}_{x, y}(l + 1) \oplus \cdots \oplus \operatorname{val}_{x, y}(r - 1) \oplus \operatorname{val}_{x, y}(r) \textsf{。} valx,y(l)⊕valx,y(l+1)⊕⋯⊕valx,y(r−1)⊕valx,y(r)。
上述公式中, ⊕ \oplus ⊕ 表示二进制按位异或。
输入格式
第一行,两个整数 n , q n, q n,q。
接下来 n − 1 n - 1 n−1 行,每行三个整数 u , v , w u, v, w u,v,w,表示 u , v u, v u,v 之间有一条权值为 w w w 的边。
接下来 q q q 行,每行四个整数 x , y , l , r x,y,l,r x,y,l,r,表示一次询问。
输出格式
输出 q q q 行,每行一个整数,为每次询问的答案。
样例 #1
样例输入 #1
3 2
1 2 1
2 3 1
1 2 1 3
2 3 2 3
样例输出 #1
1
0
提示
【样例解释】
输入给出的树如上图所示。对于点对的距离,有
- dis ( 1 , 1 ) = dis ( 1 , 3 ) = dis ( 2 , 2 ) = dis ( 3 , 1 ) = dis ( 3 , 3 ) = 0 \operatorname{dis}(1, 1) = \operatorname{dis}(1, 3) = \operatorname{dis}(2, 2) = \operatorname{dis}(3, 1) = \operatorname{dis}(3, 3) = 0 dis(1,1)=dis(1,3)=dis(2,2)=dis(3,1)=dis(3,3)=0 以及
- dis ( 1 , 2 ) = dis ( 2 , 1 ) = dis ( 2 , 3 ) = dis ( 3 , 2 ) = 1 \operatorname{dis}(1, 2) = \operatorname{dis}(2, 1) = \operatorname{dis}(2, 3) = \operatorname{dis}(3, 2) = 1 dis(1,2)=dis(2,1)=dis(2,3)=dis(3,2)=1。
第 1 1 1 问: val 1 , 2 ( 1 ) ⊕ val 1 , 2 ( 2 ) ⊕ val 1 , 2 ( 3 ) = ( 0 ⊕ 1 ) ⊕ ( 1 ⊕ 0 ) ⊕ ( 0 ⊕ 1 ) = 1 ⊕ 1 ⊕ 1 = 1 \operatorname{val}_{1, 2}(1) \oplus \operatorname{val}_{1, 2}(2) \oplus \operatorname{val}_{1, 2}(3) = (0 \oplus 1) \oplus (1 \oplus 0) \oplus (0 \oplus 1) = 1 \oplus 1 \oplus 1 = 1 val1,2(1)⊕val1,2(2)⊕val1,2(3)=(0⊕1)⊕(1⊕0)⊕(0⊕1)=1⊕1⊕1=1。
第 2 2 2 问: val 2 , 3 ( 2 ) ⊕ val 2 , 3 ( 3 ) = ( 0 ⊕ 1 ) ⊕ ( 1 ⊕ 0 ) = 1 ⊕ 1 = 0 \operatorname{val}_{2, 3}(2) \oplus \operatorname{val}_{2, 3}(3) = (0 \oplus 1) \oplus (1 \oplus 0) = 1 \oplus 1 = 0 val2,3(2)⊕val2,3(3)=(0⊕1)⊕(1⊕0)=1⊕1=0。
【数据范围】
本题采用捆绑测试。
子任务编号 | n ≤ n \le n≤ | q ≤ q \le q≤ | 分值 |
---|---|---|---|
1 | 100 100 100 | 10 10 10 | 24 |
2 | 1 0 6 10^6 106 | 10 10 10 | 14 |
3 | 100 100 100 | 1 0 6 10^6 106 | 14 |
4 | 1 0 6 10^6 106 | 1 0 6 10^6 106 | 48 |
对于 100 % 100\% 100% 的数据,保证 1 ≤ n , q ≤ 10 6 1 \le n, q \le {10}^6 1≤n,q≤106, 1 ≤ u , v , x , y ≤ n 1 \le u, v, x, y \le n 1≤u,v,x,y≤n, 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n, 0 ≤ w < 2 31 0 \le w < 2^{31} 0≤w<231。
【提示】
本题最大 I/O 量达到 60 MiB,请注意 I/O 效率。
思路
我们要知道异或的性质:
a
⊕
a
=
0
a\oplus a = 0
a⊕a=0
那么,
a
⊕
b
⊕
a
=
b
a\oplus b \oplus a = b
a⊕b⊕a=b
这一条性质很重要!
因为,本题是一棵无根树,所以,对于树上的任意点x到任一点y之间最多只有一条路径,假设为
x
−
>
i
−
>
y
x->i->y
x−>i−>y(如图):
题目中说
dis
(
x
,
y
)
\operatorname{dis}(x, y)
dis(x,y) 为
x
,
y
x,y
x,y 两点之间唯一简单路径上边权的异或和。因此:
dis
(
x
,
y
)
=
dis
(
x
,
i
)
⊕
dis
(
y
,
i
)
\operatorname{dis}(x,y) = \operatorname{dis}(x,i)\oplus \operatorname{dis}(y,i)
dis(x,y)=dis(x,i)⊕dis(y,i)
所以:
val
x
,
y
(
l
)
⊕
val
x
,
y
(
l
+
1
)
⊕
⋯
⊕
val
x
,
y
(
r
−
1
)
⊕
val
x
,
y
(
r
)
\operatorname{val}_{x, y}(l) \oplus \operatorname{val}_{x, y}(l + 1) \oplus \cdots \oplus \operatorname{val}_{x, y}(r - 1) \oplus \operatorname{val}_{x, y}(r)
valx,y(l)⊕valx,y(l+1)⊕⋯⊕valx,y(r−1)⊕valx,y(r)
=
(
dis
(
x
,
l
)
⊕
dis
(
y
,
l
)
)
⊕
(
dis
(
x
,
l
+
1
)
⊕
dis
(
y
,
l
+
1
)
)
⊕
⋯
⊕
(
dis
(
x
,
r
−
1
)
⊕
dis
(
y
,
r
−
1
)
)
⊕
(
dis
(
x
,
r
)
⊕
dis
(
y
,
r
)
)
= (\operatorname{dis}(x,l)\oplus \operatorname{dis}(y,l))\oplus (\operatorname{dis}(x,l + 1)\oplus \operatorname{dis}(y,l + 1)) \oplus \cdots \oplus(\operatorname{dis}(x,r - 1)\oplus \operatorname{dis}(y,r - 1))\oplus (\operatorname{dis}(x,r)\oplus \operatorname{dis}(y,r))
=(dis(x,l)⊕dis(y,l))⊕(dis(x,l+1)⊕dis(y,l+1))⊕⋯⊕(dis(x,r−1)⊕dis(y,r−1))⊕(dis(x,r)⊕dis(y,r))
=
dis
(
x
,
y
)
⊕
dis
(
x
,
y
)
⊕
⋯
⊕
dis
(
x
,
y
)
=\operatorname{dis}(x,y)\oplus \operatorname{dis}(x,y) \oplus \cdots \oplus\operatorname{dis}(x,y)
=dis(x,y)⊕dis(x,y)⊕⋯⊕dis(x,y)
即,
r
−
l
+
1
r - l + 1
r−l+1个
dis
(
x
,
y
)
\operatorname{dis}(x,y)
dis(x,y)进行异或。由前面的性质可以知道:
- 如果 r − l + 1 r - l + 1 r−l+1是奇数,则原式 = dis ( x , y ) =\operatorname{dis}(x,y) =dis(x,y)
- 如果 r − l + 1 r - l + 1 r−l+1是偶数,则原式 = 0 =0 =0
可见本题变为了求 dis ( x , y ) \operatorname{dis}(x,y) dis(x,y),如果用遍历求任意两点的 dis ( x , y ) \operatorname{dis}(x,y) dis(x,y)就会超时,所以可以转化为 dis ( x , y ) = dis ( x , 1 ) ⊕ dis ( y , 1 ) , \operatorname{dis}(x,y)=\operatorname{dis}(x,1)\oplus\operatorname{dis}(y,1), dis(x,y)=dis(x,1)⊕dis(y,1),因此只需要遍历求出1号点到任意点的距离,可以用堆优化的Dijkstra或者DFS来完成
代码
方法1(Dijkstra堆优化版) 时间复杂度: O ( n × l o g ( n ) + q ) O(n \times log(n) + q) O(n×log(n)+q)
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 2e6 + 10;
int n, q;
int h[N], e[N], w[N], ne[N], idx;
int l, r, x, y;
int g[N];
int dist[N], st[N];
inline int read(){
int sum = 0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch^48),ch=getchar();
return sum;
}
inline void write(int x){
if(x/10) write(x/10);
putchar(x%10^48);
}
inline void add(int a, int b, int c) // 添加一条边a->b,边权为c
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
inline void dijkstra() // 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
{
memset(dist, 0x3f, sizeof dist);
memset(st, 0, sizeof st);
priority_queue<PII, vector<PII>, greater<PII>> heap;
dist[1] = 0;
heap.push({0, 1});
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, dis = t.first;
if (st[ver]) continue;
st[ver] = 1;
for (int i = h[ver]; i != -1; i = ne[i])
{
if (w[i] != -1)
{
dist[e[i]] = w[i] ^ dis;
heap.push({dist[e[i]], e[i]});
}
}
}
}
int main()
{
cin.tie(0);
ios::sync_with_stdio(0);
memset(h, -1, sizeof h);
memset(w, -1, sizeof w);
n = read(), q = read();
int a, b, c;
for (int i = 1; i < n; i ++)
a = read(), b = read(), c = read(), add(a, b, c), add(b, a, c);
dijkstra();
for (int i = 1; i <= n; i ++)
g[i] = dist[i];
while (q --)
{
x = read(), y = read(), l = read(), r = read();
write(bool(r - l + 1 & 1) * (g[x] ^ g[y]));
putchar('\n');
}
return 0;
}
注:本题如果用该方法算时间卡的比较死,需要快读,否则会 T L E TLE TLE
方法2(DFS) 时间复杂度: O ( n + q ) O(n + q) O(n+q)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+7;
struct edge{
int nxt,v,w;
}e[N<<1];
int h[N],cnt;
inline void add_edge(int u,int v,int w){
e[++cnt].nxt=h[u],e[cnt].v=v,e[cnt].w=w;
h[u]=cnt;
}
int n,q;
int x,y,l,r;
int dis[N];
inline void dfs(int u,int fa){
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].v,w=e[i].w;
if(v==fa) continue;
dis[v]=dis[u]^w;
dfs(v,u);
}
}
signed main(){
cin.tie(0);
ios::sync_with_stdio(0);
cin >> n >> q;
for(int i=1;i<n;i++){
int u, v, w;
cin >> u >> v >> w;
add_edge(u,v,w),add_edge(v,u,w);
}
dfs(1,0);
for(int i=1;i<=q;i++){
cin >> x >> y >> l >> r;
cout << (r-l+1&1?dis[x]^dis[y]:0) << endl;
}
return 0;
}
注:此方法参考大佬AC_CSP的题解~~~
今天就到这里了!
大家有什么问题尽管提,我都会尽力回答的!最后,大年初二祝大家新年快乐!
吾欲您伸手,点的小赞赞。吾欲您喜欢,点得小关注!