2019.2.21 T2题解

meet

大概思路就是 , 找出相交的路径 , 判断方向 , 分类讨论。。

假设已经找出了相交路径 。。。
若方向相同 , 则找到相交路径上边权的最大值 , 若最大值>出发时间差 , 则可行。
原因: 由于方向相同所以在相交路径上这两个点的相对距离是不变的,看最大的边权即可。
这个东西用st表就可以搞定

若方向不同,就看看他们相遇的位置是不是相交路径上各个(不一定是最两头的点)边的端点之一。
因为题中说在边上才算 , 点不能算到边上。 所以不在点上就一定会在边上相遇。

然后就是怎么找相交路径了。。
大概有这么几种情况。。
分类讨论即可

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define int long long
const int N = 1e5+10;
inline int read()
{
    register int x = 0 , f = 0; register char c = getchar();
    while(c < '0' || c > '9') f |= c == '-' , c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return f ? -x : x;
}
int n , Q , cnt;
int head[N] , f[N][20] , st[N][20] , pre[N] , d[N] , dis[N] , Log[N];
struct edge{ int v , nex , c , id; } e[N<<1];
struct node{
    int id , l , r;
    node(int id = 0 , int l = 0 , int r = 0) : id(id) , l(l) , r(r) {}
} A[N] , B[N];
inline void add(int u , int v , int c , int id) { e[++cnt].v = v; e[cnt].nex = head[u]; e[cnt].c = c;  e[cnt].id = id; head[u] = cnt; return ; }

void dfs(int x , int fa)
{
    f[x][0] = fa; d[x] = d[fa] + 1;
    for(int i = 1 ; i < 20 ; ++i) f[x][i] = f[f[x][i-1]][i-1] , st[x][i] = max(st[x][i-1] , st[f[x][i-1]][i-1]);
    for(int i = head[x] , v ; i ; i = e[i].nex)
    {
        v = e[i].v; if(v == fa) continue;
        pre[v] = i; st[v][0] = e[i].c; dis[v] = dis[x] + e[i].c; dfs(v , x);
    }
    return ;
}

int LCA(int x , int y)
{
    if(d[x] < d[y]) swap(x , y);
    for(int i = 19 ; ~i ; --i) if(d[f[x][i]] >= d[y]) x = f[x][i];
    if(x == y) return x;
    for(int i = 19 ; ~i ; --i) if(f[x][i] != f[y][i]) x = f[x][i] , y = f[y][i];
    return f[x][0];
}

void Work(int u , int v , int t , int &tot , node *s)
{
    int p = LCA(u , v);
    while(u != p)
    {
        int i = pre[u];
        s[++tot] = node(e[i].id , t , t + e[i].c);
        t += e[i].c; u = f[u][0];
    }
    int lim = tot , T = 0;
    while(v != p)
    {
        int i = pre[v];
        s[++tot] = node(e[i].id , T , T + e[i].c);
        T += e[i].c; t += e[i].c; v = f[v][0];
    }
    for(int i = lim + 1 ; i <= tot ; ++i) s[i].l = t - s[i].l , s[i].r = t - s[i].r , swap(s[i].l , s[i].r);
    return ;
}

inline bool cmp(const node &A , const node &B) { return A.id < B.id; }

void solve1()
{
    for(int i = 1 ; i <= n ; ++i) d[i] = 0;
    dfs(1 , 0);
    int u1 , v1 , t1 , u2 , v2 , t2 , tot1 , tot2;
    while(Q--)
    {
        u1 = read(); v1 = read(); t1 = read(); tot1 = 0;
        u2 = read(); v2 = read(); t2 = read(); tot2 = 0;
        Work(u1 , v1 , t1 , tot1 , A);
        Work(u2 , v2 , t2 , tot2 , B);
        sort(A + 1 , A + 1 + tot1 , cmp);
        sort(B + 1 , B + 1 + tot2 , cmp);
        int l = 1 , r = 1 , flag = 0;
        while(l <= tot1 && r <= tot2)
        {
            if(A[l].id < B[r].id) l++;
            else if(A[l].id > B[r].id) r++;
            else
            {
                if((A[l].l <= B[r].l && B[r].l < A[l].r) || (A[l].l < B[r].r && B[r].r <= A[l].r)) { flag = 1; break; }
                l++; r++;
            }
        }
        puts(flag ? "YES" : "NO");
    }
    return ;
}

int Ask_MAX(int x , int y) // 求得x->y之间边权的最大值
{
    int ans = 0;
    if(d[x] < d[y]) swap(x , y);
    for(int i = 19 ; ~i ; --i) if(d[f[x][i]] >= d[y]) ans = max(ans , st[x][i]) , x = f[x][i];
    if(x == y) return ans;
    for(int i = 19 ; ~i ; --i) if(f[x][i] != f[y][i]) ans = max(ans , max(st[x][i] , st[y][i])) , x = f[x][i] , y = f[y][i];
    return max(ans , max(st[x][0] , st[y][0]));
}

inline int Abs(int x) { return x < 0 ? -x : x; }

#define stop { puts("NO"); return ; }
#define is_ok { puts("YES"); return ; }
void calc0(int x , int y , int t1 , int t2) // 同向
{
    if(x == y) stop
    int Max = Ask_MAX(x , y);
    puts(Abs(t1 - t2) < Max ? "YES" : "NO");
    return ;
}

int find(int x , int s) // x 往上走 s 到达的点是不是整点
{
    s = dis[x] - s;
    for(int i = 19 ; ~i ; --i) if(dis[f[x][i]] >= s) x = f[x][i];
    return dis[x] == s;
}

void calc1(int x , int y , int t1 , int t2)
{
    if(x == y) stop
    int p = LCA(x , y) , s = dis[x] + dis[y] - 2 * dis[p] , S = s + t2 - t1; // 这里的S是用来算x要走多少
    if(S < 0 || S > 2 * s) stop
    if(S & 1) is_ok
    S >>= 1; int ans = 0; // 这是真正x要走多少 , 上面那个意会。。
    if(dis[x] - dis[p] >= S) ans = find(x , S); else ans = find(y , s - S);
    puts(ans ? "NO" : "YES"); return ;

}

void solve2()
{
    dfs(1 , 0); int x1 , y1 , x2 , y2 , t1 , t2 , lx1x2 , ly1y2 , lx1y1 , lx2y2 , lx1y2 , ly1x2;
    // 各种LCA 很清楚吧
    while(Q--)
    {
        x1 = read(); y1 = read(); t1 = read();
        x2 = read(); y2 = read(); t2 = read();
        lx1y1 = LCA(x1 , y1); lx2y2 = LCA(x2 , y2);
        if(d[lx1y1] > d[lx2y2]) // 让x1y1的LCA在上面
            swap(x1 , x2), swap(y1 , y2), swap(t1 , t2), swap(lx1y1 , lx2y2);

        lx1x2 = LCA(x1 , x2); ly1y2 = LCA(y1 , y2); // 这个要写下面, 不然会GG
        lx1y2 = LCA(x1 , y2); ly1x2 = LCA(y1 , x2);
        if(lx1y1 == lx2y2) // 图1
        {
            if(lx1x2 == lx1y1 && ly1y2 == lx1y1)
                calc1(lx1y2 , ly1x2 , t1 + dis[x1] - dis[lx1y2] , t2 + dis[x2] - dis[ly1x2]); // 图1-1
            else
                calc0(lx1x2 , ly1y2 , t1 + dis[x1] - dis[lx1x2] , t2 + dis[x2] - dis[lx1x2]); // 1-2
            continue;
        }
        int lca = LCA(lx1y1 , lx2y2);
        if(lca != lx1y1) { puts("NO"); continue; } // 两条路径不相交
        if(LCA(x1 , lx2y2) == lx2y2) // 2
        {
            if(lx1x2 == lx2y2)
                calc1(lx1y2 , lx2y2 , t1 + dis[x1] - dis[lx1y2] , t2 + dis[x2] - dis[lx2y2]); //2-1
            else
                calc0(lx1x2 , lx2y2 , t1 + dis[x1] - dis[lx1x2] , t2 + dis[x2] - dis[lx1x2]); //2-2
            continue;
        }
        if(LCA(y1 , lx2y2) == lx2y2) // 3
        {
            if(ly1y2 == lx2y2)
                calc1(lx2y2 , ly1x2 , t1 + dis[x1] + dis[lx2y2] - dis[lca] * 2 , t2 + dis[x2] - dis[ly1x2]); // 3-1
            else
                calc0(lx2y2 , ly1y2 , t1 + dis[x1] + dis[lx2y2] - dis[lca] * 2 , t2 + dis[x2] - dis[lx2y2]); // 3-2
            continue;
        }
        puts("NO");
    }
    return ;
}

signed main()
{
    freopen("meet.in" , "r" , stdin);
    freopen("meet.out" , "w" , stdout);
    n = read(); Q = read();
    for(int i = 1 , u , v , c ; i < n ; ++i) u = read() , v = read() , c = read() , d[u]++ , d[v]++ , add(u , v , c , i) , add(v , u , c , i);
    if(n <= 100) solve1(); else solve2();
    fclose(stdin); fclose(stdout);
    return 0;
}
/*
8 6
1 2 3
1 3 1
1 4 2
2 5 5
2 6 1
5 7 2
5 8 4
5 3 2 7 4 2
8 6 1 1 7 6
4 5 1 4 5 10
7 8 3 3 4 5
6 7 6 5 1 2
2 1 10 8 3 3
*/

原文地址:https://www.cnblogs.com/R-Q-R-Q/p/12344081.html

时间: 2024-08-30 09:28:06

2019.2.21 T2题解的相关文章

『8.21考试题解及反思』

UNO Description 良心出题人Magolor找到了你,想要和你一起玩桌(mo)游(ni). Magolor: "杀蚂蚁?猪国杀?斗地主?麻将?立体图?哪一个好啊?" 你: "毒瘤出题人!" Magolor伤心了--"我应该给人留下一个良心出题人的印象啊!" 于是Magolor选择了众所周知的UNO.整个周游只使用UNO牌,但完全不按照UNO的规则来打.牌局有3位玩家(你.Magolor.Magolor的好朋友TTL): 每个人将会摸到

2019.10.21 csp-s模拟测试81 反思总结

T1: 把每一行状压,按行DP.设fi,j,k,i表示第几行,j是当前行的1覆盖状态,k是当前行选择按钮的状态.转移的时候枚举j和k,再枚举下一层的按钮选择情况l.如果l和j可以全覆盖当前层则转移合法,根据下一层选择l状态的代价进行转移.预处理一行每一种选法i可以覆盖到的状态di,各行选择按钮状态i对应的代价dpi,以及每一行的初始状态bi.转移时下一层的覆盖情况就是k|dl|bi+1.初始化第一层是所有选法i对应的代价,即f1,d[i]|b[1],i=dp1,i. 整个DP过程的复杂度是O(3

2019 1 21 赵松儒

1 //strlen - calculate the length of a string// 2 3 #include <stdio.h> 4 5 int strlens(const char *s); 6 char strcats(char *dest, const char *src); 7 8 #define NUM 100 9 10 int main(void) 11 { 12 char c[NUM]; 13 char *s; 14 s = c; 15 16 printf("

2019.2.15 t2

考虑倒过来计算最短路径长度,设dis[u]表示在最坏情况下,点u到最近的一 个出口的最短路,则p个出口的dis值都是0,答案即为dis[0]. 1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cmath> 5 #include <cctype> 6 #include <queue> 7 #include <algorithm&g

【比赛】【SMOJ 2019.4.21】

第三次体会到考试出超纲题的绝望-- 不要问我前两次在什么时候 \(\mathrm{T1}\) \(\color{red}{Totally\ Brute\ Force!}\) 就是个暴力-- 我们一开始假设整个数组是一个区间\([1,n]\)的区间,然后每次操作都会把区间从中间断开.(可以理解成:把中间的数删去后,左右两侧的数会分成两个区间) 这里要说一下这些区间的定义: struct Segment{int st,len;}; \(st\)表示这个区间中最小的数:\(len\)表示这个区间的长度

The Preliminary Contest for ICPC Asia Nanjing 2019/2019南京网络赛——题解

(施工中……) 比赛传送门:https://www.jisuanke.com/contest/3004 D. Robots(期望dp) 题意 给一个DAG,保证入度为$0$的点只有$1$,出度为$0$的点只有$n$. 现在一个机器人从$1$出发,每天都会以相同的概率前往相邻节点之一或静止不动. 每天机器人消耗的耐久等于经过的天数. 求机器人到点$n$期望消耗的耐久. 划水划的很愉快,唯一一道做出来的题.但是和题解做法不同(感觉我的方法麻烦),因此砸了3h在这题上面(正在试图读懂题解ing). 设

【2019.09.21】ICPC Latin American Regional-2017

提交地址:https://codeforces.com/gym/101889 解题数:6/13 题目: A: B: C: D: E: F: G: H: I: J: K: L: M: 感觉整场下来自己的贡献很低,A题看了题解是道比较综合的算法题,不只包含计算几何的内容,防ak的,暂不补.计算几何学了皮毛大概能做出中档题的时候就要收手去学其他模块的题了,加快学习进度.(zkq) 原文地址:https://www.cnblogs.com/ncu2019/p/11565382.html

2019.09.21考试报告

T1 可以发现值域的区间最多有klnk个,对于位置分块,每个块都处理出每个k的ans, 复杂度$ O(S*(n/S+klnk)+m*(n/S+S)) $ 当S=sqrt(klnk+n)时复杂度最优 T2 先把x排序 $ f[i][0/1] $代表以i为第一个端点向左/右的方案数,枚举j: 1> $ y[j]<y[i] f[i][0]+=f[j][1]; $ 2> $ y[j]>y[i] $ 枚举$ j<k<i $ && $ y[k]<y[i]  f

CSP-S2 2019 D1T2 括号树题解

说在前面的话 谨以此篇题解,纪念我初中的两年\(OI\)生涯以及不长不短的停课时光. 但愿高中还能够继续学习\(OI\)吧,也衷心希望其他\(OIer\)不要再犯类似我的错误. 题意 原题链接 给定一棵有\(n\)个节点的树,每个节点对应一个括号(左或右),需要求出从该节点到根节点的简单路径所构成的括号串中合法括号子串的数量. 其中合法括号串的定义是这样的 ()是合法括号串. 如果A是合法括号串,则(A)是合法括号串. 如果A,B是合法括号串,则AB是合法括号串. 其中\(n\le 5\time