jzoj6276. 【noip提高组模拟1】树

Time Limits: 1000 ms

Memory Limits: 524288 KB

Description

有一棵n个节点的无根树,给出其中的m对点对<x,y>。问有多少条树上的简单路径<u,v>满足该路径上不存在任何一对给出的点对<x,y>。
这里我们认为路径<u,v>和<v,u>是相同的。并且对于题目中给出的点对<x,y>满足x!=y,对于你要计数的路径<u,v>满足u!=v(即单点不算答案)。

Input

第一行两个正整数n,m。
接下来n-1行每行两个正整数u,v描述树上的一条边。
接下来m行每行两个正整数x,y描述一对给出的点对。
(注意,这里我们不保证同样的点对<x,y>不会重复出现)

Output

一行一个整数,表示满足要求的树上简单路径的条数。

Sample Input

8 3
1 2
1 3
4 8
2 4
2 5
3 6
3 7
2 3
4 8
6 7

Sample Output

11

Data Constraint

Hint

满足条件的路径为<1,2>,<1,3>,<1,4>,<1,5>,<1,6>,<1,7>,<2,4>,<2,5>,< 3,6>,< 3,7>,<4,5>。

赛时

一看到这么多的√,心想——一定可以水到巨多的分数。
看到m=1,不是送的吗?直接拿总答案减去不合法的即可。
看到菊花图,不是送的吗?如果不合法的限制一个是叶子,一个是根,则把叶子删掉。
剩下的-1即可。
看到链的情况,发现不会。
然后又发现n和m那么小,于是稳拿70.
最后发现,我?把调试程序交上去了!!!(调试输出的东西没删)
然后还没发现到限制重复。
10分妙啊♂

题解

部分分上面说过了,除了链的情况。
我们发现,对于链,直观的想法——设限制为x,y,y是x的祖宗。
那么y的祖先与x的儿子两两匹配都是不行的(显然)。
那么我们弄出一个二维平面,x轴表示从第i个点出发,y轴表示抵达第j个点。
这样就变成了在二维平面内有很多矩形,这些矩形内的点不能选。
这玩意不是扫描线吗?

即可解决,拿到90分的好成绩。

等等,既然我们想到这里了,100分不就很显然了吗?
我们对于数弄出dfn序,那么我们可以很轻松地处理出每个点对应的子树。
在二维平面内搞搞即可。
但是!如果y是x祖宗这类情况怎么办?
很简单,求出一个y到x简单路径中距离y最近的点z。
然后由于是dfn序,那么对于y以外的点就是除了z子树范围的其他点。
分成两个区间即可。

标程


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct qy
{
    int bz,sum;
};
struct kk
{
    int x,l,r,gg;
};

long long n,m,i,j,k,x,y,ans,t,l1,r1,l2,r2,sum,bz;
long long l[200005],next[200005],last[200005],tot;
long long depth[200005],dfn[200005],size[200005],fa[200005][18];
kk list[800005];
qy tree[800005];

void buildtree(long long x,long long faa)
{
    size[x]=1;
    dfn[x]=++sum;
    for (int i=1;i<=17;i++)
    {
        fa[x][i]=fa[fa[x][i-1]][i-1];
    }
    for (long long i=last[x];i>=1;i=next[i])
    {
        if (l[i]!=faa)
        {
            depth[l[i]]=depth[x]+1;
            fa[l[i]][0]=x;
            buildtree(l[i],x);
            size[x]+=size[l[i]];
        }
    }
}

void insert(long long x,long long y)
{
    l[++tot]=y;
    next[tot]=last[x];
    last[x]=tot;
}

void add(long long l1,long long r1,long long l2,long long r2)
{
    tot++;
    list[tot].l=l2;list[tot].r=r2;
    list[tot].x=l1;list[tot].gg=1;
    tot++;
    list[tot].l=l2;list[tot].r=r2;
    list[tot].x=r1+1;list[tot].gg=-1;

    tot++;
    list[tot].l=l1;list[tot].r=r1;
    list[tot].x=l2;list[tot].gg=1;
    tot++;
    list[tot].l=l1;list[tot].r=r1;
    list[tot].x=r2+1;list[tot].gg=-1;
}

int comp(kk a,kk b)
{
    return a.x<b.x;
}

int jump(int x,int steps)
{
    int y=x;
    for (int i=17;i>=0;i--)
    {
        if (depth[x]-depth[fa[y][i]]<=steps)
        {
            y=fa[y][i];
        }
    }
    return y;
}

void build(int k,int l,int r)
{
    tree[k].sum=r-l+1;
    tree[k].bz=0;
    if (l!=r)
    {
        int mid=(l+r)/2;
        build(k*2,l,mid);
        build(k*2+1,mid+1,r);
    }
}

void modify(int k,int l,int r,int x,int y,int z)
{
    if ((l<=y)&&(r>=x))
    {
        if ((l>=x)&&(r<=y))
        {
            tree[k].bz+=z;
            if (tree[k].bz==0)
            {
                if (l!=r)
                tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;
                else
                tree[k].sum=1;
            }
            else
            tree[k].sum=0;
            return;
        }
        int mid=(l+r)/2;
        modify(k*2,l,mid,x,y,z);
        modify(k*2+1,mid+1,r,x,y,z);
        if (tree[k].bz==0)
        tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;
        else
        tree[k].sum=0;
    }
}

int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for (i=1;i<=n-1;i++)
    {
        scanf("%lld%lld",&x,&y);
        insert(x,y);
        insert(y,x);
    }
    depth[1]=1;
    buildtree(1,0);
    tot=0;
    for (i=1;i<=m;i++)
    {
        scanf("%lld%lld",&x,&y);
        if (depth[x]<depth[y]) swap(x,y);
        if ((depth[x]==depth[y])||(jump(x,depth[x]-depth[y])!=y))
        {
            l1=dfn[x];
            r1=dfn[x]+size[x]-1;
            l2=dfn[y];
            r2=dfn[y]+size[y]-1;
            add(l1,r1,l2,r2);
        }
        else
        {
            t=jump(x,depth[x]-depth[y]-1);
            l1=dfn[x];
            r1=dfn[x]+size[x]-1;
            l2=1;
            r2=dfn[t]-1;
            add(l1,r1,l2,r2);
            l2=dfn[t]+size[t];
            r2=n;
            add(l1,r1,l2,r2);
        }
    }
    sort(list+1,list+1+tot,comp);
    build(1,1,n);
    bz=0;
    for (i=1;i<=n;i++)
    {
        while ((bz+1<=tot)&&(list[bz+1].x<=i))
        {
            bz++;
            modify(1,1,n,list[bz].l,list[bz].r,list[bz].gg);
        }
        ans=ans+tree[1].sum;
    }
    printf("%lld",(ans-n)/2);
}

原文地址:https://www.cnblogs.com/RainbowCrown/p/11324176.html

时间: 2024-10-13 06:20:22

jzoj6276. 【noip提高组模拟1】树的相关文章

2014-10-18 noip提高组模拟赛(codecomb)[未填]

> <看了一下觉得挺难的...除了T2 T1只想到了找环,> <倍增的思想没有学过,所以看题解看得雨里雾里的(最近真的打算学一下!) T2贪心..很容易看出的 T3感觉题目没有怎么看懂...> <正解居然是树形dp 果然不太会 T4蒟蒻> <我连最小子矩阵都不会求T_T其他更不用说了 虽然没有参加比赛,但感觉自己到不了200(hzwer说没有200应该去参加普及组QAQ) 题目出的挺好的,觉得noip极有可能出现T1T2T3,所以在此mark 而且T1以为是

10-18 noip提高组模拟赛(codecomb)T2贪心

T2:找min:一直找最小的那个,直到a[i]-x+1小于0,就找次小的,以此类推: 求max,也是一样的,一直到最大的那个,直到次大的比之前最大的大,就找次大的: 这个模拟,可以用上priority_queue: #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <queue> using namespace std; c

2017.11.25【NOIP提高组】模拟赛A组

2017.11.25[NOIP提高组]模拟赛A组 T1 3467. [NOIP2013模拟联考7]最长上升子序列(lis) T2 3468. [NOIP2013模拟联考7]OSU!(osu) T3 3472. [NOIP2013模拟联考8]匹配(match) T1 有转移方程f[i]=max{f[j]}+1,a[j]<a[i] 可以用线段树+离散化维护这个方程,因为涉及以往状态可以用主席树维护 打太丑爆空间了 Code 1 #include<cstdio> 2 #include<c

2017.12.02【NOIP提高组】模拟赛A组

2017.12.02[NOIP提高组]模拟赛A组 T1 3555[GDKOI2014模拟]树的直径 T2 3542[清华集训2014]冒泡排序 T3 3486[NOIP2013模拟联考10]道路改建(rebuild) T1 树直径的一个性质,两棵树合并,形成新的树的直径的两个端点为原树中的四个端点之二. 可以用反证法证明.用此性质本题就变成了lca裸题了 Code #include<cstdio> #include<cstring> #include<cmath> #i

2017.12.09【NOIP提高组】模拟赛A组

2017.12.09[NOIP提高组]模拟赛A组 T1 3489. [NOIP2013模拟联考11]数列的GCD(gcd) T2 3500.[NOIP2013模拟联考15]物语(monogatari) T3 3501.[NOIP2013模拟联考15]消息传递(news) 吐槽:这次的题好像有点水啊,但最简单的第二题都给打挂啦!!(数组开小了) T1 本套题中最难的题.考虑dp 设f[i]是b[1],b[2]...b[N]的最大公约数的数目,g[i]是b[1],b[2]...b[N]的公约数的数目

ZROI提高组模拟赛05总结

ZROI提高组模拟赛05总结 感觉是目前为止最简单的模拟赛了吧 但是依旧不尽人意... T1 有一半的人在30min前就A掉了 而我花了1h11min 就是一个简单的背包,我硬是转化了模型想了好久,生生把一个弱智题变成了一个不可做题 最后竟然在转化两次后的模型上以为自己做出来了 这个题比别人多花的1h左右的时间,而且只得到了30分,成为了这场比赛失败的关键因素 T2 依旧是一道简单题 有人20min之内就A掉了 感觉放在CF里最多算一道Div2 D,还是简单的那种 可是我又一次想复杂了 大意就是

NOIP 提高组2013 火柴排队 (Vijos P1842)

描述 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度.现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑ i=1 n (a i ?b i ) 2  ,其中 a i   表示第一列火柴中第 i 个火柴的高度,b i   表示第二列火柴中第 i 个火柴的高度. 每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小.请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模

Vijos P1002 过河 (NOIP提高组2005)

链接:https://www.vijos.org/p/1002 解析: 若 p*x+(p+1)*y=Q(采用跳跃距离p和p+1时可以跳至任何位置Q),则在Q ≥ P*(P-1)时是一定有解的. 由于题目给出的一个区间是1≤S≤T≤10,于是当相邻的两个石子之间的距离不小于8*9=72时,则后面的距离都可以到达,我们就可以认为它们之间的距离就是72.如此一来,我们就将原题L的范围缩小为了100*72=7200,动态规划算法完全可以承受了. 但是当S=T时,上述等式是无法使用的,在这种情况下,只需要

NOIP提高组2004 合并果子题解

NOIP提高组2004 合并果子题解 描述:在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了.多多在合并果子时总共消耗的体力等于每次合并所耗体力之和. 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力.假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出