分析

可以发现第一列和最后一列永远不会被删除,于是我们可以想到维护前后缀最小生成树,但是直接维护的话显然时间空间两爆炸。(通过上网找题解)可以发现我们关心的只是最左边和最右边两列,而不关心内部的连边情况。所以我们可以仅维护这两列的节点在最小生成树上形成的虚树,边权是对应链上最大的边权,合并时对两棵虚树上的所有边再跑一遍最小生成树就好了。

由于虚树的大小是\(O(n)\)级别的,所以该算法的时间复杂度为\(O(n(m+q) \log n)\)。

(这代码写起来有点恶心。)

代码

#include <bits/stdc++.h>

#define rin(i,a,b) for(int i=(a);i<=(b);++i)
#define irin(i,a,b) for(int i=(a);i>=(b);--i)
#define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
#define Size(a) (int)a.size()
#define pb push_back
#define mkpr std::make_pair
#define fi first
#define se second
#define lowbit(a) ((a)&(-(a)))
typedef long long LL; using std::cerr;
using std::endl; inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
} const int MAXN=105;
const int MAXM=10005; int n,m,q;
unsigned int SA, SB, SC;int lim; int getweight() {
SA ^= SA << 16;
SA ^= SA >> 5;
SA ^= SA << 1;
unsigned int t = SA;
SA = SB;
SB = SC;
SC ^= t ^ SA;
return SC % lim + 1;
} struct mst_edge{
int u,v,w;
inline friend bool operator < (mst_edge x,mst_edge y){
return x.w<y.w;
}
}; struct mst{
LL sum;
std::vector<int> poi;
std::vector<mst_edge> edg;
inline void init(){sum=0;poi.clear();edg.clear();}
}; mst pre[MAXM],suf[MAXM];
std::vector<mst_edge> hng[MAXM],shu[MAXM],ext; inline int calc_id(int x,int y){
return (x-1)*m+y;
} int dsu[MAXN*MAXM];
int ecnt,head[MAXN*MAXM];
bool mark[MAXN*MAXM],havem[MAXN*MAXM];
std::vector<int> po,retp;
std::vector<mst_edge> ee,rete; struct Edge{
int to,nxt,w;
}e[MAXN*MAXM*4]; int getf(int x){
return dsu[x]==x?x:dsu[x]=getf(dsu[x]);
} inline bool merge_dsu(int x,int y){
x=getf(x),y=getf(y);
if(x!=y){
dsu[y]=x;
return true;
}
return false;
} inline void add_edge(int bg,int ed,int val){
++ecnt;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
e[ecnt].w=val;
head[bg]=ecnt;
} void dfs1(int x,int pre){
havem[x]|=mark[x];
trav(i,x){
int ver=e[i].to;
if(ver==pre)continue;
dfs1(ver,x);
havem[x]|=havem[ver];
}
} void dfs2(int x,int pre,int las,int maxw){
int flag=0;
trav(i,x){
int ver=e[i].to;
if(ver==pre)continue;
if(havem[ver])++flag;
}
if(flag>1)mark[x]=true;
if(mark[x]){
retp.pb(x);
if(las)rete.pb((mst_edge){las,x,maxw});
}
trav(i,x){
int ver=e[i].to;
if(ver==pre)continue;
if(mark[x])dfs2(ver,x,x,e[i].w);
else dfs2(ver,x,las,std::max(maxw,e[i].w));
}
} mst merge(mst L,mst R,int bg){
mst ret;ret.init();
ret.sum=L.sum+R.sum;
po.clear();ee.clear();
retp.clear();rete.clear();ecnt=0;
rin(i,0,Size(L.poi)-1)po.pb(L.poi[i]);
rin(i,0,Size(R.poi)-1)po.pb(R.poi[i]);
rin(i,0,Size(L.edg)-1){
ret.sum-=L.edg[i].w;
ee.pb(L.edg[i]);
}
rin(i,0,Size(R.edg)-1){
ret.sum-=R.edg[i].w;
ee.pb(R.edg[i]);
}
rin(i,0,Size(ext)-1)ee.pb(ext[i]);
rin(i,0,Size(po)-1){
dsu[po[i]]=po[i];
head[po[i]]=0;
mark[po[i]]=havem[po[i]]=false;
}
std::sort(ee.begin(),ee.end());
int temp=0;
rin(i,0,Size(ee)-1){
if(merge_dsu(ee[i].u,ee[i].v)){
++temp;
ret.sum+=ee[i].w;
add_edge(ee[i].u,ee[i].v,ee[i].w);
add_edge(ee[i].v,ee[i].u,ee[i].w);
if(temp==Size(po)-1)break;
}
}
rin(i,1,n)mark[calc_id(i,bg)]=true;
if(bg==1)rin(i,0,Size(R.poi)-1)mark[R.poi[i]]=true;
else rin(i,0,Size(L.poi)-1)mark[L.poi[i]]=true;
dfs1(po[0],0);dfs2(po[0],0,0,0);
ret.poi=retp,ret.edg=rete;
return ret;
} int main(){
n=read(),m=read(),SA=read(),SB=read(),SC=read(),lim=read();
rin(i,1,n)rin(j,1,m){
int w=getweight();
if(j<m)hng[j].pb((mst_edge){calc_id(i,j),calc_id(i,j+1),w});
else hng[j].pb((mst_edge){calc_id(i,j),calc_id(i,1),w});
}
rin(i,1,n-1)rin(j,1,m){
int w=getweight();
shu[j].pb((mst_edge){calc_id(i,j),calc_id(i+1,j),w});
}
rin(i,1,n)pre[1].poi.pb(calc_id(i,1));
rin(i,0,Size(shu[1])-1){
pre[1].sum+=shu[1][i].w;
pre[1].edg.pb(shu[1][i]);
}
rin(i,2,m){
ext.clear();
rin(j,1,n)pre[i].poi.pb(calc_id(j,i));
rin(j,0,Size(shu[i])-1){
pre[i].sum+=shu[i][j].w;
pre[i].edg.pb(shu[i][j]);
}
rin(j,0,Size(hng[i-1])-1)ext.pb(hng[i-1][j]);
pre[i]=merge(pre[i-1],pre[i],1);
}
rin(i,1,n)suf[m].poi.pb(calc_id(i,m));
rin(i,0,Size(shu[m])-1){
suf[m].sum+=shu[m][i].w;
suf[m].edg.pb(shu[m][i]);
}
irin(i,m-1,1){
ext.clear();
rin(j,1,n)suf[i].poi.pb(calc_id(j,i));
rin(j,0,Size(shu[i])-1){
suf[i].sum+=shu[i][j].w;
suf[i].edg.pb(shu[i][j]);
}
rin(j,0,Size(hng[i])-1)ext.pb(hng[i][j]);
suf[i]=merge(suf[i],suf[i+1],m);
}
ext.clear();
rin(i,0,Size(hng[m])-1)ext.pb(hng[m][i]);
q=read();
while(q--){
int l=read(),r=read();
printf("%lld\n",merge(pre[l-1],suf[r+1],1).sum);
}
return 0;
}

[VIJOS2053][SDOI2019]世界地图:最小生成树+虚树的更多相关文章

  1. CF891C Envy 最小生成树/虚树

    正解:最小生成树/虚树 解题报告: 传送门! sd如我就只想到了最暴力的想法,一点儿优化都麻油想到,,,真的菜到爆炸了QAQ 然后就分别港下两个正解QAQ 法一,最小生成树 这个主要是要想到关于最小生 ...

  2. [SDOI2019]世界地图(kruskal重构树+虚树)

    通过子任务1.3十分显然,子任务4实际上就是线段树,和我下午写的[SDOI2015]道路修建一模一样,堪称“我抄我自己”,不会的可以先做一下这个题. 然后考虑正解,参考了zhoushuyu的博客,首先 ...

  3. 【题解】Luogu P5360 [SDOI2019]世界地图

    原题传送门 每次查询的实际就是将地图的一个前缀和一个后缀合并后的图的最小生成树边权和 我们要预处理每个前缀和后缀的最小生成树 实际求前缀和(后缀和)的过程珂以理解为上一个前缀和这一列的最小生成树进行合 ...

  4. 洛谷 P6199 - [EER1]河童重工(点分治+虚树)

    洛谷题面传送门 神仙题. 首先看到这样两棵树的题目,我们肯定会往动态树分治的方向考虑.考虑每次找出 \(T_2\) 的重心进行点分治.然后考虑跨过分治中心的点对之间的连边情况.由于连边边权与两棵树都有 ...

  5. BZOJ 2286 消耗战 (虚树+树形DP)

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...

  6. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

  7. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

  8. BZOJ 2286 树链剖分+DFS序+虚树+树形DP

    第一次学习虚树,就是把无关的点去掉.S里维护一条链即可. #include <iostream> #include <cstring> #include <cstdio& ...

  9. 青云的机房组网方案(简单+普通+困难)(虚树+树形DP+容斥)

    题目链接 1.对于简单的版本n<=500, ai<=50 直接暴力枚举两个点x,y,dfs求x与y的距离. 2.对于普通难度n<=10000,ai<=500 普通难度解法挺多 ...

随机推荐

  1. linux centos 7.3 编译安装mysql5.7

    #安装依赖 yum update yum install -y gcc gcc-c++ make libtool zlib zlib-devel openssl openssl-devel pcre ...

  2. 图灵机器人API调用 C++版

    这是一个非常简单的例子,作为新手的我是拿来练手的,当然也可以给和我一样的朋友一些参考. 而且图灵官网没有给出C的例子,网上一搜也是各种Java.C#甚至易语言实现,不要歧视C++好不好●︿●,就算不如 ...

  3. java native本地方法详解(转)

    文章链接出处: 详解native方法的使用 自己实现一个Native方法的调用 JNI 开始本篇的内容之前,首先要讲一下JNI.Java很好,使用的人很多.应用极 广,但是Java不是完美的.Java ...

  4. 剑指offer-正则表达式匹配-字符串-python****

    # -*- coding:utf-8 -*- ''' 题目:请实现一个函数用来匹配包括'.'和'*'的正则表达式. 模式中的字符'.'表示任意一个字符(不包括空字符!),而'*'表示它前面的字符可以出 ...

  5. luogu P5366 [SNOI2017]遗失的答案

    luogu 首先gcd为\(G\),lcm为\(L\),有可能出现的数(指同时是\(G\)的因数以及是\(L\)的倍数)可以发现只有几百个.如果选出的数要能取到gcd,那么对于每种质因子,都要有一个数 ...

  6. luogu P3620 [APIO/CTSC 2007]数据备份

    luogu 首先如果一条线不是了连接的相邻两个位置一定不优,把它拆成若干连接相邻位置的线.所以现在问题是有\(n\)个物品,选\(k\)个,要求选的位置不能相邻,求最小总和 如果没有选的位置不能相邻这 ...

  7. Windows7/win10系统安装JDK的环境变量设置javac不是内部命令或外部命令

    ---恢复内容开始--- Windows7/win10系统安装JDK的环境变量设置 Windows7 X64安装“jdk-6u26-windows-x64.exe”后,按照网上的环境变量设置方法设置了 ...

  8. Scala新版本学习(1):

    1.进官网:https://www.scala-lang.org/ 上面就是进入Scala社区后的一个画面,官方对Scala的简单介绍是:Scala将面向对象和函数式编程集合在一个简洁的高级语言中,S ...

  9. 第二十篇 jQuery 初步学习2

    jQuery 初步学习2   前言:   老师这里啰嗦一下,因为考虑到一些同学,不太了解WEB前端这门语言.老师就简单的说一下,写前端,需要什么:一台笔记本.一个文本编辑器.就没啦!当然,写这门语言, ...

  10. QQ恶搞 - 让艾特你的人语无伦次

    效果图: 实现过程: 代码: ‮ 将上面的代码复制添加到你的群名片后面即可. 原理解析: 这个代码是一个Unicode控制字符 - RLO,它可以控制在它后面的所有文本都已倒序的方式显示.在qq群艾特 ...