首页 > 其他分享 >[Tkey] [IOI 2018] werewolf

[Tkey] [IOI 2018] werewolf

时间:2024-08-18 17:16:09浏览次数:8  
标签:werewolf int Kruskal 400001 back dfs vector IOI Tkey

注意看,我耗时五个小时 AK 了 IOI

题意

给你一个图,每次给定若干询问 \((s,t,l,r)\),请你完成下述要求:

  • 定义 \(S\) 为到 \(s\) 的最短路径不小于 \(l\) 的点构成的子图,\(T\) 为到 \(t\) 的最短路径不大于 \(r\) 的点构成的子图
  • 请你判断 \(S\) 与 \(T\) 是否有交集

解法

当询问次数不大时做法显然,每次对 \(s,t\) 跑 dfs 即可

但是此题询问量大,考虑如何预处理

一 寻找子图

路径最小最大,容易想到使用 Kruskal 生成树. 我们尝试对整张图建立 Kruskal 最小和最大生成树,根据其性质在树上跑 LCA 来 \(\log\) 解决这部分问题.

但是在实际实现的时候会遇到一些问题:我们仍然还需要枚举所有点,因此最坏情况下需要单次 \(n\log n\) 求解,复杂度不够优秀.

现在转化问题:注意到我们求解出来的子图一定是树结构,由 dfs 序的性质,最终这颗子树一定是原树 dfs 序上连续的一段,因此我们将问题抽象成这样:

给定两个序列 \(S,T\),询问是否存在 \(i\in[l,n]\),使得存在 \(T_{j}=S_{i},j\in[1,r]\)

其中 \(S\) 是我们建立的 Kruskal 最小生成树的 dfs 序,\(T\) 是建立的 Kruskal 最大生成树的 dfs 序,这样我们就通过两遍 Kruskal+LCA 转化了这个问题

二 求解交集

注意到我们可以建立一个数组 \(f_{i}\) 表示 \(i\) 在 \(S\) 中的位置,这样建立一个映射是为了方便后续操作:我们可以通过调用 \(f_{T_{i}}\) 来查看 \(T_{i}\) 在 \(S\) 中的位置

因此,我们可以进一步转化这个问题:

给定两个序列 \(S,T\),询问是否存在 \(i\in[1,r]\),使得 \(f_{T_{i}}\in[l,n]\)

注意到我们现在把问题转化成了一种类似求值域的东西,因此考虑对 \(f_{T_{i}}\) 开可持久化线段树,维护前缀和,每次把 \(r,l-1\) 两颗可持久化线段树传下去作差维护答案,这个问题即可求解.

下面是这一部分,可持久化线段树内的 ask() 函数代码

bool ask(int p,int q,int l,int r,int L,int R){ //存在则返回 true
    if(l>R or L>r) return false;
    if(L<=l and r<=R) return t[q].cnt-t[p].cnt; //满足[1,r]条件后还需要作差
    int mid(l,r);
    return ask(t[q].l,t[p].l,l,mid,L,R) or ask(t[q].r,t[p].r,mid+1,r,L,R);
}

比较简单。

三 整合

那我们刚才弄出来的 Kruskal 生成树显然就是用来找,我们需要的 dfs 序的左右端点(也即哪两颗可持久化线段树需要被传下去)了,这一部分暴力倍增即可。

注意事项

  • 在 loj 版里,需要引用 "werewolf.h"
  • 求端点的倍增别弄反了,应该是先大再小
  • 因为用了 Kruskal 生成树,因此空间需要开到 \(2n\)

代码

#include<bits/stdc++.h>
#include"werewolf.h"
using namespace std;
const int inf=0x7fffffff;
int n,m,q;
struct edge{
    int from,to;
};
edge ed[400001];
int a[400001],b[400001];
int cnt=0;
bool cmp1(const edge &A,const edge &B){
    return max(A.from,A.to)<max(B.from,B.to);
}
bool cmp2(const edge &A,const edge &B){
    return min(A.from,A.to)>min(B.from,B.to);
}
int mp[400001],t[400001],root[400001];
class dsu{
    private:
    int fa[400001];
    public:
    void clear(){
        for(int i=1;i<=2*n;++i){
            fa[i]=i;
        }
    }
    int find(int id){
        if(id==fa[id]) return id;
        fa[id]=find(fa[id]);
        return fa[id];
    }
    void join(int x,int y){ //add x to y (fa[x]=y)
        int fx=find(x),fy=find(y);
        if(fx!=fy){
            fa[fx]=fy;
        }
    }
};
class kruskal{
    public:
    int tot,treecnt;
    int fa[20][400001],w[400001],l[400001],r[400001];
    vector<int>e[400001];
    dsu d;
    void clear(){
        tot=n;
        d.clear();
        memset(l,0x3f,sizeof l);
    }
    void dfs(int now,int type){
        if(now<=n){
            if(type==1){
                a[++treecnt]=now;
            }
            else{
                b[++treecnt]=now;
            }
            l[now]=r[now]=treecnt;
            return;
        }
        for(int i:e[now]){
            if(i==fa[0][now]) continue;
            fa[0][i]=now;
            dfs(i,type);
            l[now]=min(l[now],l[i]);
            r[now]=max(r[now],r[i]);
        }
    }
    void build(int type){
        clear();
        if(type==1){
            sort(ed+1,ed+m+1,cmp1);
        }
        else{
            sort(ed+1,ed+m+1,cmp2);
        }
        for(int i=1;i<=m;++i){
            if(d.find(ed[i].from)!=d.find(ed[i].to)){
                e[++tot].push_back(d.find(ed[i].from));
                e[tot].push_back(d.find(ed[i].to));
                e[d.find(ed[i].from)].push_back(tot);
                e[d.find(ed[i].to)].push_back(tot);
                d.join(ed[i].from,tot);
                d.join(ed[i].to,tot);
                if(type==1){
                    w[tot]=max(ed[i].from,ed[i].to);
                }
                else{
                    w[tot]=min(ed[i].from,ed[i].to);
                }
            }
        }
        if(type==1) w[0]=inf;
        else w[0]=-inf;
        dfs(tot,type);
        for(int j=1;j<=19;++j){
            for(int i=1;i<=tot;++i){
                fa[j][i]=fa[j-1][fa[j-1][i]];
            }
        }
    }
    int get_version(int now,int l,int r){
        for(int i=19;~i;--i){
            if(w[fa[i][now]]>=l and w[fa[i][now]]<=r){
                now=fa[i][now];
            }
        }
        return now;
    }
}t1,t2;
class HIST_Stree{
    public:
    #define mid(l,r) mid=((l)+(r))/2
    int tot=0;
    struct tree{
        int l,r;
        int cnt;
    }t[400001*25];
    int clone(int id){
        t[++tot]=t[id];
        return tot;
    }
    int insert(int id,int l,int r,int pos){
        int q=clone(id);
        if(l==r){
            t[q].cnt++;
            return q;
        }
        int mid(l,r);
        if(pos<=mid){
            t[q].l=insert(t[id].l,l,mid,pos);
        }
        else{
            t[q].r=insert(t[id].r,mid+1,r,pos);
        }
        t[q].cnt=t[t[q].l].cnt+t[t[q].r].cnt;
        return q;
    }
    bool ask(int p,int q,int l,int r,int L,int R){
        if(l>R or L>r) return false;
        if(L<=l and r<=R) return t[q].cnt-t[p].cnt;
        int mid(l,r);
        return ask(t[q].l,t[p].l,l,mid,L,R) or ask(t[q].r,t[p].r,mid+1,r,L,R);
    }
}hist;
vector<int> check_validity(int N, vector<int> X, vector<int> Y, vector<int> U, vector<int> V, vector<int> L, vector<int> R) {
	n=N,m=X.size(),q=U.size();
    vector<int>ans;
    for(int i=1;i<=m;++i){
        ed[i]={X[i-1]+1,Y[i-1]+1};
    }
    t1.build(1);
    t2.build(2);
    for(int i=1;i<=n;++i){
        t[a[i]]=i;
    }
    for(int i=1;i<=n;++i){
        mp[i]=t[b[i]];
    }
    for(int i=1;i<=n;++i){
        root[i]=hist.insert(root[i-1],1,n,mp[i]);
    }
    for(int i=1;i<=q;++i){
        int s=U[i-1]+1,t=V[i-1]+1;
        int l=L[i-1]+1,r=R[i-1]+1;
        swap(s,t);
        s=t1.get_version(s,1,r);
        t=t2.get_version(t,l,n);
        if(hist.ask(root[t2.r[t]],root[t2.l[t]-1],1,n,t1.l[s],t1.r[s])){
            ans.push_back(1);
        }
        else{
            ans.push_back(0);
        }
    }
    return ans;
}
// vector<int> X,Y,U,V,L,R;
// int main(){
//     cin>>n>>m>>q;
//     for(int i=1;i<=m;++i){
//         int a,b;
//         cin>>a>>b;
//         X.push_back(a);Y.push_back(b);
//     }
//     for(int i=1;i<=q;++i){
//         int s,e,l,r;
//         cin>>s>>e>>l>>r;
//         U.push_back(s);
//         V.push_back(e);
//         L.push_back(l);
//         R.push_back(r);
//     }
//     vector<int> ans=check_validity(n,X,Y,U,V,L,R);
//     for(int i:ans){
//         cout<<i<<endl;
//     }
// }

标签:werewolf,int,Kruskal,400001,back,dfs,vector,IOI,Tkey
From: https://www.cnblogs.com/HaneDaCafe/p/18365822

相关文章

  • P8518 [IOI2021] 分糖果 题解
    DescriptionKhong阿姨正在给附近一所学校的学生准备\(n\)盒糖果。盒子的编号分别为\(0\)到\(n-1\),开始时盒子都为空。第\(i\)个盒子\((0\leqi\leqn-1)\)至多可以容纳\(c[i]\)块糖果(容量为\(c[i]\))。Khong阿姨花了\(q\)天时间准备糖果盒。在第\(j\)天......
  • OpenCV(cv::waitKey())
    目录1.函数解析参数返回值2.示例3.说明4.注意事项cv::waitKey()是OpenCV库中的一个函数,用于等待用户的键盘输入。它在处理图像和视频时非常有用,特别是在显示图像窗口时,用于控制图像的显示和响应用户输入。1.函数解析intcv::waitKey(intdelay=0);参数delay:......
  • ; 每隔10分钟定时关闭并重启蘑菇游戏下载器,防止下载器卡死宕机死机停止下载的AutoHot
     ;每隔10分钟定时关闭并重启蘑菇游戏下载器,防止下载器卡死宕机死机停止下载的AutoHotkey脚本2024年8月7日  ;每隔10分钟定时关闭并重启蘑菇游戏下载器,防止下载器卡死宕机死机停止下载的AutoHotkey脚本2024年8月7日;测试环境:AutoHotkey_1.1.37.02_Setup.exe&Win......
  • [Tkey] CF1526B I Hate 1111
    给定一个数,将它表示成若干个形如\(11,111,1111\cdots\)之类的数之和,判断有没有可行解考虑到一种贪心,即从高位开始依次向下减去每位数字,判断还能不能减动,减不动或者没减完就报告无解.显然这样的贪心仅在\(11,111,1111\cdots\)的出现次数之和不超过\(9\)时是稳定正确的,一......
  • [USACO1.5] [IOI1994]数字三角形 Number Triangles
    传送锚点[P1216USACO1.5][IOI1994]数字三角形NumberTriangles-洛谷|计算机科学教育新生态(luogu.com.cn)题目[USACO1.5][IOI1994]数字三角形NumberTriangles题目描述观察下面的数字金字塔。写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最......
  • IOI2022 邮局 加强版 题解
    [IOI2000]邮局加强版题解考虑动态规划,设\(f_{i,j}\)为经过了\(i\)个村庄,正在建第\(j\)​个邮局的最优距离。以及\(w_{i,j}\)表示区间\([i,j]\)​内建一个邮局时的距离总和。\(a\)数组表示每个村庄的坐标编号。朴素版状态转移方程:\[f_{i,j}=\min(f_{i,j},f_{k,j......
  • [Tkey] Transport Nekomusume II
    CL-20考虑定义一条有向边\(u\rightarrowv\)的意义为\(u\)把窝让给了\(v\),那么每个点一定入度为\(1\),所有的边会形成一个外向基环树森林。贪心地把猫娘按照权值从大到小排序,每个猫娘看成一条无向边,那么可行的方案一定会形成一个基环树森林。用并查集维护所有窝组成的基环......
  • [Tkey] 黑兔子,白兔子
    CL-21一般拿到这个题第一眼都应该能看出并查集,subtask1是给并查集暴力修改的.后面subtask2没有联通操作,是给纯线段树的,也算是启发正解了再往下可以考虑操作\(1\)采用线段树区间修改,操作\(2\)采用并查集维护的思路.按这个思路去想,那么操作\(2\)肯定不能进行修改,因为我......
  • ControlMyMonitor、MultiMonitorTool、autohotkey 设置笔记本和台式机切换屏幕
    一、背景1.1台笔记本、1台台式机共用一个显示器。2.显示器1个vga输入、1个hdmi输入3.笔记本通过hdmi转vga连到显示器,台式机通过HDMI连到显示器二、需求通过键盘切换显示器输入。三、软件介绍ControlMyMonitor:控制显示器输入方式(选择vga、hdmi)MultiMonitorTool:控制电脑在哪......
  • 【题解】P4648 [IOI2007] pairs 动物对数
    Problem给定模板\(B(1\leB\le3)\),代表\(B\)维空间。其中有\(n\)个点,给出坐标与坐标上限\(M\),求\(n\)个点中曼哈顿距离\(\leD\)的对数。Solve\(B=1\)考虑将问题化简成:求\(\sum\limits_{i=1}^n\sum\limits_{j=1}^{i-1}[dis(i,j)\leqD]\)。其中\(dis(i,j)\)......