[Luogu3242][HNOI2015]接水果

Luogu
我今天做两道整体二分结果全都是BZOJ权限题???

sol

我们抓住“盘子的路径是水果的路径的子路径”这个条件。
考虑每一个盘子路径\((u,v)\),讨论它可以作为哪些水果路径的子路径。
如果说\(u,v\)不是祖孙关系,那么水果路径的两端点就必须分别在以\(u\)\(v\)为根的子树中。即若一个水果路径\((a,b)\)满足\((u,v)\)是它的子路径,则有\(dfn_u\le{dfn_a}\le{low_u},dfn_v\le{dfn_b}\le{low_v}\)(请自行判断是否交换\(u,v\)\(a,b\)的顺序)
其中\(low_u\)是以\(u\)为根的子树中的最大\(dfn\)序。
如果\(u,v\)是祖孙关系,假设\(u\)是祖先,那么据路径的一端一定要在\(v\)的子树里,另一端的位置,要保证不在\(w\)的子树里,其中\(w\)\(u\)的直接儿子也是\(v\)的祖先(当然只有那一个啦)。
所以就是要满足\(1\le{dfn_a}<dfn_w\mbox{或}low_u<dfn_a\le{n},dfn_v\le{dfn_b}\le{low_v}\)
发现这个类似一个二维的矩形呀,所以就可以做一个扫描线,树状数组统计答案就可以了。
把所有盘子视为一个或是两个矩形,按权值大小排序,每次二分一个位置判断某一点(一个水果)是否已经被\(k\)个矩形覆盖,然后向下递归即可。
依旧是整体二分的板子

code

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 40005;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
struct edge{int to,next;}a[N<<1];
struct plate{
    int x1,x2,y1,y2,v;
    bool operator < (const plate &b) const
        {return v<b.v;}
}p[N<<1];
struct fruit{
    int x,y,k,id;
    bool operator < (const fruit &b) const
        {return x<b.x;}
}q[N],q1[N],q2[N];
struct node{
    int x,y,v;
    bool operator < (const node &b) const
        {return x<b.x;}
}zsy[N<<2];
int n,P,Q,head[N],cnt,fa[N],dep[N],sz[N],son[N],top[N],dfn[N],low[N],tot,c[N],ans[N];
void dfs1(int u,int f)
{
    fa[u]=f;dep[u]=dep[f]+1;sz[u]=1;
    for (int e=head[u];e;e=a[e].next)
    {
        int v=a[e].to;if (v==f) continue;
        dfs1(v,u);
        sz[u]+=sz[v];if (sz[v]>sz[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int up)
{
    top[u]=up;dfn[u]=++cnt;
    if (son[u]) dfs2(son[u],up);
    for (int e=head[u];e;e=a[e].next)
        if (a[e].to!=fa[u]&&a[e].to!=son[u])
            dfs2(a[e].to,a[e].to);
    low[u]=cnt;
}
int getlca(int u,int v)
{
    while (top[u]^top[v])
    {
        if (dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]<dep[v]?u:v;
}
int getson(int u,int v)
{
    int gg;
    while (top[u]^top[v]) gg=top[v],v=fa[top[v]];
    return u==v?gg:son[u];
}
void modify(int k,int v){while (k<=n) c[k]+=v,k+=k&-k;}
int query(int k){int s=0;while (k) s+=c[k],k-=k&-k;return s;}
void solve(int L,int R,int l,int r)//LR询问(水果)区间 lr二分答案区间
{
    if (L>R) return;
    if (l==r)
    {
        for (int i=L;i<=R;i++)
            ans[q[i].id]=p[l].v;
        return;
    }
    int mid=l+r>>1,top=0,pos=0,t1=0,t2=0,temp;
    for (int i=l;i<=mid;i++)
    {
        zsy[++top]=(node){p[i].x1,p[i].y1,1};
        zsy[++top]=(node){p[i].x1,p[i].y2+1,-1};
        zsy[++top]=(node){p[i].x2+1,p[i].y1,-1};
        zsy[++top]=(node){p[i].x2+1,p[i].y2+1,1};
    }
    sort(zsy+1,zsy+top+1);
    for (int i=L;i<=R;i++)
    {
        while (pos<top&&zsy[pos+1].x<=q[i].x)
            pos++,modify(zsy[pos].y,zsy[pos].v);
        temp=query(q[i].y);
        if (q[i].k<=temp) q1[++t1]=q[i];
        else q[i].k-=temp,q2[++t2]=q[i];
    }
    while (pos<top) pos++,modify(zsy[pos].y,zsy[pos].v);//记得这里要清空树状数组
    for (int i=L,j=1;j<=t1;i++,j++) q[i]=q1[j];
    for (int i=L+t1,j=1;j<=t2;i++,j++) q[i]=q2[j];
    solve(L,L+t1-1,l,mid);solve(L+t1,R,mid+1,r);
}
int main()
{
    n=gi();P=gi();Q=gi();
    for (int i=1,u,v;i<n;i++)
    {
        u=gi();v=gi();
        a[++cnt]=(edge){v,head[u]};head[u]=cnt;
        a[++cnt]=(edge){u,head[v]};head[v]=cnt;
    }
    dfs1(1,0);cnt=0;dfs2(1,1);
    for (int i=1,u,v,w,gg;i<=P;i++)
    {
        u=gi();v=gi();w=gi();
        if (dfn[u]>dfn[v]) swap(u,v);
        if (getlca(u,v)==u)
        {
            gg=getson(u,v);
            if (dfn[gg]>1) p[++tot]=(plate){1,dfn[gg]-1,dfn[v],low[v],w};
            if (low[gg]<n) p[++tot]=(plate){dfn[v],low[v],low[gg]+1,n,w};
        }
        else p[++tot]=(plate){dfn[u],low[u],dfn[v],low[v],w};
    }
    sort(p+1,p+tot+1);
    for (int i=1,u,v,k;i<=Q;i++)
    {
        u=gi();v=gi();k=gi();
        if (dfn[u]>dfn[v]) swap(u,v);
        q[i]=(fruit){dfn[u],dfn[v],k,i};
    }
    sort(q+1,q+Q+1);
    solve(1,Q,1,tot);
    for (int i=1;i<=Q;i++) printf("%d\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/8365346.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值