[ZJOI2019]语言
树链剖分入门题吧
一个非常直观的想法是使用树剖将一条链拆成\(log^2n\)个矩形,套用矩形面积并算法即可得到一个垃圾的3个log过不去算法
为了得到一个两个log的做法,我们观察一下拆出来的矩形的性质
首先是一堆跨越对角线的矩形,这一部分可以维护每个对角线处延伸出来的最大值线性得出
接下来如果我们令dfs序小的去数dfs序大的点,那么我们会发现矩形的第二维全部是重链的前缀
因此线段树可以被替换成每个重链上的multiset
此时的复杂度依然是3个log,仍然需要优化
接下来发现第一维是一段区间的矩形仅仅有\(O(nlogn)\)个,于是仅仅对这一部分开multiset维护,
剩余的都是前缀,倒着扫一遍重链然后使用一个变量记录最大值即可了
#include<cstdio>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;const int N=1e5+10;typedef long long ll;
int v[N<<1];int x[N<<1];int ct;int al[N];ll ans;ll tot;
inline void add(int u,int V){v[++ct]=V;x[ct]=al[u];al[u]=ct;}
int dfn[N];int nfd[N];int df;int top[N];int fa[N];
int h[N];int siz[N];int dep[N];int n;int m;
inline int dfs1(int u,int f)
{
for(int i=al[u];i;i=x[i])
if(v[i]!=f)
dep[v[i]]=dep[u]+1,fa[v[i]]=u,siz[u]+=dfs1(v[i],u),
h[u]=(siz[h[u]]<siz[v[i]])?v[i]:h[u];
return ++siz[u];
}
inline void dfs2(int u,int f)
{
dfn[++df]=u;nfd[u]=df;top[u]=top[u]?top[u]:u;
if(h[u])top[h[u]]=top[u],dfs2(h[u],u);
for(int i=al[u];i;i=x[i])
if(v[i]!=f&&v[i]!=h[u])dfs2(v[i],u);
}
namespace solver1
{
int mx;int add;int len[N];
inline void push(int x){mx=max(mx,x-add);}
inline void ins(int st,int le){
len[st]=max(len[st],le);}
inline void solve()
{
for(int i=1;i<=n;i++)
{push(len[i]);add--;ans+=max(0,mx+add);}
}
}
struct data{int id;int len;};
inline int mabs(int x){return (x<0)?-x:x;}
struct adv_pq
{
multiset <int> s;int mx;
inline void clear(){s.clear();mx=0;}
inline ll top()
{return (s.empty())?mx:max(mx,*s.rbegin());}
inline void push(data a)
{
tot-=top();
if(a.id<0)s.insert(a.len);else mx=max(mx,a.len);
tot+=top();
}
inline void pop(data a)
{
tot-=top();
s.erase(s.find(a.len));
tot+=top();
}
};
namespace solver2
{
vector <data> ins[N];vector <data> del[N];adv_pq su[N];
inline void solvechain(int l,int r)
{
tot=0;
for(int i=r;i>=l;i--)
{
for(vector <data> :: iterator it=ins[i].begin();it!=ins[i].end();++it)
su[mabs(it->id)].push(*it);
ans+=tot;
for(vector <data> :: iterator it=del[i].begin();it!=del[i].end();++it)
su[mabs(it->id)].pop(*it);
}
for(int i=r;i>=l;i--)
for(vector <data> :: iterator it=ins[i].begin();it!=ins[i].end();++it)
su[mabs(it->id)].clear();
}
inline void ins_rec(int fl,int fr,int id,int len)
{
if(top[dfn[fl]]==dfn[fl])
ins[fr].push_back((data){id,len});
else
ins[fr].push_back((data){-id,len}),
del[fl].push_back((data){-id,len});
}
}
int qu1[N];int qu2[N];int len1[N];int len2[N];
int hd1;int hd2;
inline void split(int u,int v)
{
hd1=0;hd2=0;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]])
qu2[++hd2]=v,v=fa[top[v]];
else
qu1[++hd1]=u,u=fa[top[u]];
}
if(nfd[u]<nfd[v])swap(u,v);
if(hd1&&hd2)
{
if(nfd[top[qu1[hd1]]]>nfd[top[qu2[hd2]]])
{
for(int i=1;i<=max(hd1,hd2);i++)swap(qu1[i],qu2[i]);
swap(hd1,hd2);
}
}
for(int i=1;i<=hd1;i++)
{
int mu=qu1[i];int mv=top[mu];
len1[i]=nfd[mu]-nfd[mv]+1;
solver1::ins(nfd[mv],len1[i]);
solver2::ins_rec(nfd[v],nfd[u],mv,len1[i]);
}
for(int i=1;i<=hd2;i++)
{
int mu=qu2[i];int mv=top[mu];
len2[i]=nfd[mu]-nfd[mv]+1;
solver1::ins(nfd[mv],len2[i]);
solver2::ins_rec(nfd[v],nfd[u],mv,len2[i]);
}
solver1::ins(nfd[v],nfd[u]-nfd[v]+1);
for(int i=1;i<=hd1;i++)
{
int mu=qu1[i];int mv=top[mu];
for(int j=1;j<i;j++)
solver2::ins_rec(nfd[mv],nfd[mu],top[qu1[j]],len1[j]);
for(int j=1;j<=hd2;j++)
solver2::ins_rec(nfd[mv],nfd[mu],top[qu2[j]],len2[j]);
}
for(int i=1;i<=hd2;i++)
{
int mu=qu2[i];int mv=top[mu];
for(int j=1;j<i;j++)
solver2::ins_rec(nfd[mv],nfd[mu],top[qu2[j]],len2[j]);
}
}
bool book[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<n;i++)
scanf("%d%d",&u,&v),add(u,v),add(v,u);
dfs1(1,0);dfs2(1,0);
for(int i=1,u,v;i<=m;i++)
scanf("%d%d",&u,&v),split(u,v);
solver1::solve();
for(int i=1;i<=n;i++)
{
if(book[i])continue;
int p=dfn[i];int cnt=0;
for(;p;p=h[p])book[nfd[p]]=true,cnt++;
solver2::solvechain(i,i+cnt-1);
}
printf("%lld",ans);
}
[ZJOI2019]语言的更多相关文章
- 题解 P5327 [ZJOI2019]语言
P5327 [ZJOI2019]语言 解题思路 暴力 首先讲一下我垃圾的 40pts 的暴力(其他 dalao 都是 60pts 起步): 当然评测机快的话(比如 LOJ 的),可以卡过 3,4 个点 ...
- 【题解】Luogu P5327 [ZJOI2019]语言
原题传送门 看到这种树上统计点对个数的题一般是线段树合并,这题也不出意外 先对这棵树进行树剖,对于每次普及语言,在\(x,y\)两点的线段树上的\(x,y\)两位置打\(+1\)标记,在点\(fa[l ...
- P5327 [ZJOI2019]语言
一边写草稿一边做题吧.要看题解的往下翻,或者是旁边的导航跳一下. 草稿 因为可以开展贸易活动的条件是存在一种通用语 \(L\) 满足 \(u_i\) 到 \(v_i\) 的最短路径上都会 \(L\) ...
- [ZJOI2019]语言[树链的并、线段树合并]
题意 题目链接 分析 考虑枚举每个点的答案,最后除以 2 即可. 可以与 \(u\) 构成合法点对 的集合 为所有经过了 \(u\) 的链的并.因为这些链两两有交,根据结论 "树上两条相交的 ...
- Luogu P5327 [ZJOI2019]语言
ZJOI2019Day2的温暖题,然后考场上只会大常数的\(O(n\log^3 n)\),就懒得写拿了60pts走人 首先我们简化题意,容易发现每个点能到达的点形成了一个联通块,我们只需要统计出这个联 ...
- [Luogu5327][ZJOI2019]语言(树上差分+线段树合并)
首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案.根据实现不同有下面几种方法.三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路 ...
- Luogu5327 ZJOI2019语言(树上差分+线段树合并)
暴力树剖做法显然,即使做到两个log也不那么优美. 考虑避免树剖做到一个log.那么容易想到树上差分,也即要对每个点统计所有经过他的路径产生的总贡献(显然就是所有这些路径端点所构成的斯坦纳树大小),并 ...
- [ZJOI2019]语言——树剖+树上差分+线段树合并
原题链接戳这儿 SOLUTION 考虑一种非常\(naive\)的统计方法,就是对于每一个点\(u\),我们维护它能到达的点集\(S_u\),最后答案就是\(\frac{\sum\limits_{i= ...
- [LOJ3046][ZJOI2019]语言:树链的并+线段树合并
分析 问题显然可以转化为对于每个节点询问所有这个节点的所有链的链并的大小. 考场上我直接通过树剖打标记+树剖线段树维护以\(O(n \log^3 n)\)的时间复杂度暴力实现了这个过程.(使用LCT或 ...
随机推荐
- Open SuSE 安装Python3.6
1. 下载Python3.6 tar包 去除Modules/Setup文件167行的注释 readline readline.c -lreadline -ltermcap 2. 下载readline- ...
- linux使用privoxy将55转为http代理
=============================================== 2018/3/21_第3次修改 ccb_warlock 更新 ...
- ReactiveX 学习笔记(22)使用 RxJS + Angular 进行 GUI 编程
课题 程序界面由3个文本编辑框和1个文本标签组成. 要求文本标签实时显示3个文本编辑框所输入的数字之和. 文本编辑框输入的不是合法数字时,将其值视为0. 3个文本编辑框的初值分别为1,2,3. 创建工 ...
- json 异常
com.google.gson.JsonSyntaxException: 1530842820000 1530842820000 是服务器直接返回的Date值由 Gson 解析后出来的值. 后台发出: ...
- py2与py3区别总结
1. py2中的str是py3中的bytes py2中的Unicode是py3中的str 声明一个字符串变量时,py2 和py3都是str类型,但py2代表字节类型,py3代表文本类型 隐式转换: p ...
- MFC笔记6
1.MFC文件的读写操作 写操作 创建一个编辑框(IDC_INFOR_EDIT1),在里面输入信息,创建一个按钮(IDC_BUTTON),点击按钮会触发(OnBnClickedButton2()函数) ...
- python中的字符串
一.在python中,字符串是不可变类型 通过以下代码说明: >>> s = 'hello, world' >>> id(s) 2108634288304 > ...
- Linux云服务器
1. 第一步:前往阿里云官网注册账号,实名认证.进去云服务器,创建实例! 第二步:选配置,**公网IP地址选择“分配”!**如果你是首次购买主机,安全组先不必勾选,或者勾选默认的 .后面,会有安全组的 ...
- Kettle (5) - 获取 Web 数据
使用 Kettle 获取网页数据.当然,这里的网页数据主要指结构化数据,可能是 xml 格式.json 格式 或者 csv 文件等.以 http://services.odata.org/V3/Nor ...
- CRM中QueryDict和模型表知识补充
CRM中QueryDict和模型表知识补充 1.QueryDict的用法 request.GET的用法:1.在页面上输入:http://127.0.0.1:8000/index/print(reque ...