zoukankan      html  css  js  c++  java
  • [loj6734]图上的游戏

    考虑原图是一条链的情况——

    思路:随机一个点$x$,将其所在段(边集)再划分为两段,重复此过程即可得到该链

    实现上,(从左到右)维护每一段的左端点和边集,二分找到最后一个删除后$x$到根不连通的段,那么其即是$x$所在段,再暴力枚举段中每一条边划分即可

    前者二分显然为$o(nlog n)$,后者即是一个类似于sort/treap的结构,总期望次数为$o(nlog n)$

    最终,总查询次数为$o(nlog n)$,可以通过

    考虑原图是一棵树的情况——

    思路:以0为根建树,求出一个链剖分的结果(链上存储点集、边集和链顶父亲所在的链),再利用链的做法求出链上点和边具体的顺序,最后将所有链组合即可得到该树

    实现上,根据思路分为两部分(求链剖分和求原树):

    求链剖分时,对每一条链维护边集、部分点集(仅考虑已经加入的点)和链顶父亲所在的链,并按照剖分顺序依次编号,那么加入一个点$x$,按以下方式处理——

    找到剩余边中在$x$到根路径上的边,这可以通过不断二分实现(即找到第一条满足删除后$x$到根不连通的边,直接二分不具备单调性,那么将其前缀均删除即可),接下来有两种情况:

    1.不存在这样的边,即$x$在某条链上,二分找到编号最大的链满足删除后$x$到根不连通,那么其即是$x$所在链,将$x$加入该链的点集即可

    2.存在这样的边,这些边必然是$x$到根路径上的一个前缀,即得到了一条新的链,而此时1中的二分即会求出该链链顶父亲所在的链

    (这里直接二分同样不具备单调性,将其后缀均删除即可)

    求原树时,对每一条链在其链顶的父亲所在的链中二分找到具体的父亲即可

    除了求链剖分中"不断二分/分治"外,其余均为直接二分显然为$o(nlog n)$,前者考虑每条边至多被得到一次,进而均摊总询问次数为$o(nlog n)$

    最终,总查询次数为$o(nlog n)$,可以通过

    结合上面两个做法,来考虑原问题——

    思路:求出一棵生成树,再求出所有非树边

    实现上,根据思路同样分为两部分(求生成树和非树边):

    求生成树时,可以参考树的做法,唯一不同的即在于$x$到根路径并不唯一,那么改为找到第一条满足将其以及其之前的边均删除后$x$到根不连通的边即可(这与之前的二分实现上一样)

    (可以理解为从前往后尽量删去当前边,并通过二分优化此过程)

    另外,当前的非树边默认已经被删除,下同

    求非树边时,按dfs序从后往前考虑点$x$,一条非树边以$x$为一个端点当且仅当将$x$到父亲的边删除、将这条边加入后$x$到根不连通,同样可以二分实现

    找到边后,还需要求出该边另一个端点,也即求dfs序最大的点满足将其和$x$到父亲的边删除、将这条边加入后$x$到根仍连通,同样也可以二分实现

    (这里的两个二分同样不具备单调性,将其前缀/后缀均加入/删除即可)

    重复此过程也即可得到所有非树边,询问次数的分析与之前类似

    最终,总查询次数为$o(mlog n)$,可以通过

      1 #include<bits/stdc++.h>
      2 #include"graph.h"
      3 using namespace std;
      4 #define N 605
      5 #define pii pair<int,int>
      6 int n,m,t,vis[N],fa[N],Fa[N],dfn[N];
      7 vector<int>Vis,v[N],e[N];
      8 vector<pii>ans;
      9 void clear(int p){
     10     for(int i=0;i<m;i++)Vis[i]=p;
     11 }
     12 void init_tree(){
     13     clear(0);
     14     for(int i=0;i<m;i++)
     15         if (vis[i]==1)Vis[i]=1;
     16 }
     17 int find1(int k){
     18     vector<int>v;
     19     for(int i=0;i<m;i++)
     20         if (!vis[i])v.push_back(i);
     21     clear(1);
     22     for(int i=0;i<v.size();i++)Vis[v[i]]=0;
     23     if (query(k,Vis))return -1;
     24     int l=0,r=(int)v.size()-1;
     25     while (l<r){
     26         int mid=(l+r>>1);
     27         clear(1);
     28         for(int i=0;i<=mid;i++)Vis[v[i]]=0;
     29         if (query(k,Vis))l=mid+1;
     30         else r=mid;
     31     }
     32     return v[l];
     33 }
     34 int find2(int k){
     35     init_tree();
     36     for(int i=1;i<t;i++)
     37         for(int j=0;j<e[i].size();j++)Vis[e[i][j]]=0;
     38     if (query(k,Vis))return 0;
     39     int l=1,r=t-1;
     40     while (l<r){
     41         int mid=(l+r+1>>1);
     42         init_tree();
     43         for(int i=mid;i<t;i++)
     44             for(int j=0;j<e[i].size();j++)Vis[e[i][j]]=0;
     45         if (query(k,Vis))r=mid-1;
     46         else l=mid;
     47     }
     48     return l;
     49 }
     50 int find3(int k,int x){
     51     int l=0,r=(int)e[k].size()-1;
     52     while (l<r){
     53         int mid=(l+r+1>>1);
     54         init_tree();
     55         Vis[e[k][mid]]=0;
     56         if (query(x,Vis))r=mid-1;
     57         else l=mid;
     58     }
     59     return v[k][l];
     60 }
     61 int find4(int k){
     62     vector<int>v;
     63     for(int i=0;i<m;i++)
     64         if (!vis[i])v.push_back(i);
     65     init_tree();
     66     for(int i=0;i<v.size();i++)Vis[v[i]]=1;
     67     Vis[Fa[k]]=0;
     68     if (!query(k,Vis))return -1;
     69     int l=0,r=(int)v.size()-1;
     70     while (l<r){
     71         int mid=(l+r>>1);
     72         init_tree();
     73         for(int i=0;i<=mid;i++)Vis[v[i]]=1;
     74         Vis[Fa[k]]=0;
     75         if (query(k,Vis))r=mid;
     76         else l=mid+1;
     77     }
     78     return v[l];
     79 }
     80 int find5(int k,int x){
     81     int l=1,r=n;
     82     while (l<r){
     83         int mid=(l+r+1>>1);
     84         init_tree();
     85         for(int i=mid;i<=n;i++)Vis[Fa[dfn[i]]]=0;
     86         Vis[x]=1;
     87         if (query(k,Vis))r=mid-1;
     88         else l=mid;
     89     }
     90     return dfn[l];
     91 }
     92 void calc(int k){
     93     vector<int>v0,fa[N];
     94     random_shuffle(v[k].begin()+1,v[k].end());
     95     v0.push_back(v[k][0]);
     96     for(int i=0;i<e[k].size();i++)fa[v[k][0]].push_back(e[k][i]);
     97     for(int i=1;i<v[k].size();i++){
     98         init_tree();
     99         int l=0,r=(int)v0.size()-1;
    100         while (l<r){
    101             int mid=(l+r+1>>1);
    102             init_tree();
    103             for(int j=0;j<fa[v0[mid]].size();j++)Vis[fa[v0[mid]][j]]=0;
    104             if (query(v[k][i],Vis))r=mid-1;
    105             else l=mid;
    106         }
    107         vector<int>e0;
    108         for(int j=0;j<fa[v0[l]].size();j++){
    109             init_tree();
    110             Vis[fa[v0[l]][j]]=0;
    111             if (query(v[k][i],Vis))e0.push_back(fa[v0[l]][j]);
    112             else fa[v[k][i]].push_back(fa[v0[l]][j]);
    113         }
    114         fa[v0[l]]=e0;
    115         v0.insert(v0.begin()+l,v[k][i]);
    116     }
    117     v[k]=v0,e[k].clear();
    118     for(int i=0;i<v[k].size();i++)e[k].push_back(fa[v0[i]][0]);
    119 }
    120 vector<pii> solve(int nn,int mm){
    121     srand(time(0));
    122     n=nn,m=mm;
    123     for(int i=0;i<m;i++){
    124         Vis.push_back(0);
    125         ans.push_back(make_pair(0,0));
    126     }
    127     for(int i=1;i<n;i++){
    128         t++;
    129         while (1){
    130             int x=find1(i);
    131             if (x<0)break;
    132             vis[x]=1,e[t].push_back(x);
    133         }
    134         int pos=find2(i);
    135         if (e[t].empty())t--,v[pos].push_back(i);
    136         else fa[t]=pos,v[t].push_back(i);
    137     }
    138     for(int i=1;i<=t;i++)calc(i);
    139     for(int i=1;i<=t;i++)
    140         if (fa[i])fa[i]=find3(fa[i],v[i][0]);
    141     dfn[0]=1,dfn[1]=0;
    142     for(int i=1;i<=t;i++)
    143         for(int j=0;j<v[i].size();j++){
    144             Fa[v[i][j]]=e[i][j];
    145             dfn[++dfn[0]]=v[i][j];
    146         }
    147     for(int i=1;i<=t;i++){
    148         ans[e[i][0]]=make_pair(fa[i],v[i][0]);
    149         for(int j=1;j<v[i].size();j++)ans[e[i][j]]=make_pair(v[i][j-1],v[i][j]);
    150     }
    151     for(int i=dfn[0];i>1;i--)
    152         while (1){
    153             int x=find4(dfn[i]);
    154             if (x<0)break;
    155             vis[x]=-1,ans[x]=make_pair(dfn[i],find5(dfn[i],x));
    156         }
    157     return ans;
    158 }
    View Code
  • 相关阅读:
    开始Flask项目
    夜间模式的开启与关闭,父模板的制作
    从首页问答标题到问答详情页
    首页列表显示全部问答,完成问答详情页布局
    制作首页的显示列表
    发布功能完成
    登录之后更新导航
    完成登录功能,用session记住用户名
    完成注册功能
    通过用户模型,对数据库进行增删改查操作
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/15493965.html
Copyright © 2011-2022 走看看