Description
小J继续着周游世界的旅程,这次他来到了一个神奇的群岛。这片群岛有n个岛屿,同时这些岛屿被标上了1-n的编号。
每个岛屿上面都有神奇的传送门,传送门可以把小J从当前的岛屿u传送到指定的岛屿v上面,同时在对应的岛屿v上也有一个与之相对应的传送门,可以使用这个传送门从岛屿v回到岛屿u。
细心的小J发现了传送门有一些小字“这些岛屿上面总的有2*(n-1)个传送门,将这些岛屿连接起来,一个传送门最多只能使用一次,即只能帮助你从岛屿u到岛屿v,使用一次之后传送门就会消失。”
同时,小J也发现了使用一次传送门之后能够得到一定的金币,但使用一对相对应的传送门能得到的金币的数量是不相同的,即从岛屿u到岛屿v能得到的金币和从岛屿v到岛屿u能得到的金币是不同的。
现在小J想知道如果他一开始在岛屿x上,想要借助传送门前往岛屿y,途中最多能收集多少金币。(到达岛屿y之后小J还可以继续前往别的岛屿,只要保证他最后能回到岛屿y就行)。
Input
输入:第一行一个T,代表数据组数。
第二行一个n,表示岛屿的数量。
接下来n-1行,每行有4个数,u,v,c1,c2,表示使用从岛屿u到岛屿v的传送门能得到c1个金币,使用从岛屿v到岛屿u的传送门能得到c2个金币。
接下来一行一个q。代表询问次数。
接下来q行,每行两个数x和y,代表小L现在在岛屿x上,想前往岛屿y。
数据范围T<=10
n<=100000
1<=u,v<=n,1<=c1,c2<=100000
q<=100000
1<x,y<=n
Output
输出:输出q行
对于每次询问,输出正确答案。
Sample Input 1
1 5 1 2 5 10 3 5 25 3 4 2 15 12 3 2 6 7 2 1 5 4 3
Sample Output 1
64 65
思路:
寒假耗费了大量时光,到现在才学会LCA,真是菜得罪有应得。
虽然当时看出来,这是一个树结构,但是对他也没有什么好的办法来解决,所以只好事后来补题。
先用两遍Dijkstra,求出根节点到每个节点的最短路,以及其反向边的最短路。然后,利用LCA,求出每次询问的两个节点的最近公共父节点。
设LCA为t,询问的节点是x,y。那么t到x的最短路,就是根节点到x的最短路,减去根节点到t的最短路。求反向边的最短路,是因为x到y的路径,有一段是向上的,有一段是向下的。
答案就是所有路径的总和,减去x到y的路径的反向边的权值总和
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<vector> #include<queue> using namespace std; typedef long long ll; const int maxn = 1e5+6; const ll inf = 2e11; vector<int>u[maxn]; int bin[30]; int fa[maxn][30]; vector<ll>w[maxn],wt[maxn]; ll dis1[maxn],dis2[maxn]; int deep[maxn]; ll sum; struct node { int x; ll dis; bool operator<(const node x)const { return x.dis<dis; } }; bool vis[maxn]; void get_bin() { for(int i=0;i<20;i++){ bin[i]=(1<<i); } } void build() { int n; scanf("%d",&n); int x,y; ll z1,z2; for(int i=1;i<=n-1;i++){ scanf("%d%d%lld%lld",&x,&y,&z1,&z2); u[x].push_back(y); w[x].push_back(z1); wt[x].push_back(z2); u[y].push_back(x); wt[y].push_back(z1); w[y].push_back(z2); sum+=z1+z2; } } void bfs() { memset(vis,0,sizeof(vis)); queue<int>q; q.push(1); deep[1]=1; int cur; vis[1]=true; while(!q.empty()){ cur=q.front();q.pop(); vis[cur]=true; for(int i=1;i<20;i++){ if(bin[i]>deep[cur]){break;} fa[cur][i]=fa[fa[cur][i-1]][i-1]; } int siz=u[cur].size(); for(int i=0;i<siz;i++){ int t=u[cur][i]; if(vis[t]){continue;} fa[t][0]=cur; deep[t]=deep[cur]+1; q.push(t); } } } void Dijkstra1() { memset(vis,0,sizeof(vis)); priority_queue<node>q; q.push(node{1,0ll}); node exa; dis1[1]=0; int cur,t; while(!q.empty()){ exa=q.top();q.pop(); t=exa.x; if(vis[t]){continue;} vis[t]=0; int siz=u[t].size(); for(int i=0;i<siz;i++){ if(dis1[u[t][i]]>dis1[t]+w[t][i]){ dis1[u[t][i]]=dis1[t]+w[t][i]; q.push(node{u[t][i],dis1[u[t][i]]}); } } } } void Dijkstra2() { memset(vis,0,sizeof(vis)); priority_queue<node>q; q.push(node{1,0ll}); node exa; dis2[1]=0; int cur,t; while(!q.empty()){ exa=q.top();q.pop(); t=exa.x; if(vis[t]){continue;} vis[t]=0; int siz=u[t].size(); for(int i=0;i<siz;i++){ if(dis2[u[t][i]]>dis2[t]+wt[t][i]){ dis2[u[t][i]]=dis2[t]+wt[t][i]; q.push(node{u[t][i],dis2[u[t][i]]}); } } } } int lca(int x,int y) { if(deep[x]<deep[y]){swap(x,y);} int t=deep[x]-deep[y]; for(int i=0;i<=20;i++){ if(bin[i]&t){x=fa[x][i];} } for(int i=20;i>=0;i--){ if(fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; } } if(x==y){return x;} else return fa[x][0]; } void solve() { bfs(); Dijkstra1(); Dijkstra2(); int q; scanf("%d",&q); int x,y; ll ans; for(int i=1;i<=q;i++){ scanf("%d%d",&x,&y); int t=lca(x,y); ans=sum-(dis1[x]-dis1[t])-(dis2[y]-dis2[t]); printf("%lld ",ans); } } void init() { for(int i=0;i<maxn;i++){ wt[i].clear(); w[i].clear(); u[i].clear(); dis1[i]=inf; dis2[i]=inf; } memset(deep,0,sizeof(deep)); memset(fa,0,sizeof(fa)); sum=0; } int main() { int T; scanf("%d",&T); get_bin(); while(T--){ init(); build(); solve(); } return 0; }
第一发没有清空fa数组,WA了。
其实我不是很理解,为什么要清空fa数组,因为fa在一租新的数据中,要用到的都有自然更新。。。