一边写草稿一边做题吧。要看题解的往下翻,或者是旁边的导航跳一下。

草稿

因为可以开展贸易活动的条件是存在一种通用语 \(L\) 满足 \(u_i\) 到 \(v_i\) 的最短路径上都会 \(L\) 。所以我们考虑能够带来贡献的,只有同一次传教。

但是很有可能在进行当前这一次传教过程中,中间的两座城市已经可以进行贸易往来了,所以我们需要将这些部分的贡献给减去。因为是要其中都有同一种语言,所以我们只需要找连续的有相同语言的就可以了。

相当于对于一次传教,我们需要找出传教路径上的相同颜色的链的长度的城市对。这个东西搞得不好好像要变成 \(O(n^2)\) 。

哦,我好像会了,维护一个结构体,表示当前区间的这个答案是多少,同时从左边开始的链的颜色和长度,右边开始的链的颜色和长度,然后合并的时候就判断一下是不是相同颜色什么的就可以了。

就这?就这?


错了错了,没有这么简单。

我们需要考虑一个区间被多计算了几次,用容斥来做。我们考虑维护一个线段树,表示这个点是否被计算过,连续的一段表示这些点两两都被计算过。

我们考虑添加一条于当前表示计算过的有交的区间该怎么加,应该就直接减去这段区间中被计算过的和再加上这段区间能带来的贡献就好了吧。

这个东西应该还是可以用线段树来维护的。

就这?就这?


错了错了,我错了……

得到了一点提示,对于每一个点,若在一次染色过程中经过了该点,就在该点的集合里添加这两个点的端点,最后求每个点对应集合的最小生成树大小。

嗯,看上去可做多了,接下来考虑怎么去搞这个东西。

因为对于一次染色操作,我们是对这一条链上的所有点的集合中都塞入这两个点,所以我们可以考虑线段树合并。

那么现在就相当于我们有一个点的集合的点(存在线段树里),我们如何求他的最小生成树呢?

我们可以再开一个线段树,处理区间加,最后求有多少个节点的权值是大于 \(0\) 的。因为你每一次添加一对节点,最多在这两个节点的链上产生贡献。然后统计答案,最后除以 \(2\) 就可以了。

然后我们发现前面处理集合的这一棵线段树甚至可以不需要,只需要合并区间加的线段树就可以了吧。。。

复杂度的话区间加的线段树的操作个数是 \(O(n\log_2n)\) 的,然后每一次操作是复杂度是 \(O(\log_2n)\) 的,同时产生 \(O(\log_2n)\) 个节点。所以最终的复杂度应该是 \(O(n\log_2^2n)\) 的。

呼,终于好了。

题解

稍微理一下。

我们对于每一个,其贡献就是经过这个点的链的并。这个东西是可以用类似扫描线的东西来维护的。

然后对于两两节点之间的转移,只用线段树合并和树上差分的思路来完成的。

复杂度是 \(O(n\log_2^2n)\) 的,跑的还挺快,但是线段树合并很容易时空间假掉,需要注意!!!

代码如下

#include<bits/stdc++.h>
using namespace std;
#define Lint long long
const int N=1e5+5;
int n,m;
struct Edge{int nxt,to;}e[N<<1];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
struct Node{int dep,fa,top,son,size;}tr[N];
int dfn[N],mp[N],cnt=0;
void dfs1(int u)
{
tr[u].size=1,tr[u].top=u;
for(int i=fir[u];i;i=e[i].nxt)
{
if(e[i].to==tr[u].fa) continue;
tr[e[i].to].fa=u;
tr[e[i].to].dep=tr[u].dep+1;
dfs1(e[i].to);
tr[u].size+=tr[e[i].to].size;
if(tr[e[i].to].size>tr[tr[u].son].size)
tr[u].son=e[i].to;
}
}
void dfs2(int u)
{
dfn[++cnt]=u,mp[u]=cnt;
if(tr[u].son)
{
tr[tr[u].son].top=tr[u].top;
dfs2(tr[u].son);
}
for(int i=fir[u];i;i=e[i].nxt)
{
if(e[i].to==tr[u].fa||e[i].to==tr[u].son) continue;
dfs2(e[i].to);
}
}
struct Seg_Tree
{
int rt[N],now,top,rub[N<<5];
struct Node{int ls,rs,data,tag,alive;}tr[N<<5];
Seg_Tree(){now=top=0,memset(rub,0,sizeof(rub));}
int newnode(){return top?rub[top--]:++now;}
void recycle(int x){tr[x]=(Node){0,0,0,0,0},rub[++top]=x;}
void up(int u,int l,int r)
{
if(tr[u].tag) tr[u].data=r-l+1;
else tr[u].data=tr[tr[u].ls].data+tr[tr[u].rs].data;
}
void copy(int u,int v)
{
tr[u]=tr[v];
tr[tr[u].ls].alive++;
tr[tr[u].rs].alive++;
}
void add(int u,int l,int r,int x,int y,int z)
{
if(x<=l&&r<=y){tr[u].tag+=z,up(u,l,r);return ;}
int mid=(l+r)>>1;
if(x<=mid)
{
if(!tr[u].ls) tr[u].ls=newnode(),tr[tr[u].ls].alive++;
add(tr[u].ls,l,mid,x,y,z);
}
if(y>mid)
{
if(!tr[u].rs) tr[u].rs=newnode(),tr[tr[u].rs].alive++;
add(tr[u].rs,mid+1,r,x,y,z);
}
up(u,l,r);
}
void del(int u)
{
if(tr[u].ls) tr[tr[u].ls].alive--;
if(tr[u].rs) tr[tr[u].rs].alive--;
if(tr[u].ls&&!tr[tr[u].ls].alive) del(tr[u].ls);
if(tr[u].rs&&!tr[tr[u].rs].alive) del(tr[u].rs);
recycle(u);
}
void merge(int u,int v,int l,int r)
{
tr[u].tag+=tr[v].tag;
int mid=(l+r)>>1;
if(tr[v].ls)
{
if(!tr[u].ls) tr[u].ls=newnode(),copy(tr[u].ls,tr[v].ls);
else merge(tr[u].ls,tr[v].ls,l,mid);
}
if(tr[v].rs)
{
if(!tr[u].rs) tr[u].rs=newnode(),copy(tr[u].rs,tr[v].rs);
else merge(tr[u].rs,tr[v].rs,mid+1,r);
}
up(u,l,r);
}
}t;
void work(int rt,int u,int v,int z)
{
while(tr[u].top!=tr[v].top)
{
if(tr[tr[u].top].dep<tr[tr[v].top].dep) swap(u,v);
t.add(rt,1,n,mp[tr[u].top],mp[u],z);
u=tr[tr[u].top].fa;
}
if(tr[u].dep>tr[v].dep) swap(u,v);
t.add(rt,1,n,mp[u],mp[v],z);
}
int lca(int u,int v)
{
while(tr[u].top!=tr[v].top)
{
if(tr[tr[u].top].dep<tr[tr[v].top].dep) swap(u,v);
u=tr[tr[u].top].fa;
}
if(tr[u].dep>tr[v].dep) swap(u,v);
return u;
}
vector<pair<pair<int,int>,int> > bag[N];
Lint res=0;
void dfs(int u)//n
{
t.rt[u]=t.newnode();
for(int i=fir[u];i;i=e[i].nxt)
{
if(e[i].to==tr[u].fa) continue;
dfs(e[i].to);
t.merge(t.rt[u],t.rt[e[i].to],1,n);//log_2^2n
t.del(t.rt[e[i].to]);//log_2^2n
}
for(int i=0;i<(int)bag[u].size();++i)
work(t.rt[u],bag[u][i].first.first,bag[u][i].first.second,bag[u][i].second);
if(t.tr[t.rt[u]].data) res+=t.tr[t.rt[u]].data-1;
}
int main()
{
cin>>n>>m;
for(int i=1,u,v;i<n;++i)
{
scanf("%d%d",&u,&v);
add(u,v,i<<1),add(v,u,i<<1|1);
}
dfs1(1),dfs2(1);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
int tmp=lca(u,v);
bag[u].push_back(make_pair(make_pair(u,v),1));
bag[v].push_back(make_pair(make_pair(u,v),1));
bag[tmp].push_back(make_pair(make_pair(u,v),-1));
bag[tr[tmp].fa].push_back(make_pair(make_pair(u,v),-1));
}
dfs(1);
printf("%lld\n",res/2);
return 0;
}

P5327 [ZJOI2019]语言的更多相关文章

  1. 题解 P5327 [ZJOI2019]语言

    P5327 [ZJOI2019]语言 解题思路 暴力 首先讲一下我垃圾的 40pts 的暴力(其他 dalao 都是 60pts 起步): 当然评测机快的话(比如 LOJ 的),可以卡过 3,4 个点 ...

  2. 【题解】Luogu P5327 [ZJOI2019]语言

    原题传送门 看到这种树上统计点对个数的题一般是线段树合并,这题也不出意外 先对这棵树进行树剖,对于每次普及语言,在\(x,y\)两点的线段树上的\(x,y\)两位置打\(+1\)标记,在点\(fa[l ...

  3. Luogu P5327 [ZJOI2019]语言

    ZJOI2019Day2的温暖题,然后考场上只会大常数的\(O(n\log^3 n)\),就懒得写拿了60pts走人 首先我们简化题意,容易发现每个点能到达的点形成了一个联通块,我们只需要统计出这个联 ...

  4. [ZJOI2019]语言

    树链剖分入门题吧 一个非常直观的想法是使用树剖将一条链拆成\(log^2n\)个矩形,套用矩形面积并算法即可得到一个垃圾的3个log过不去算法 为了得到一个两个log的做法,我们观察一下拆出来的矩形的 ...

  5. [ZJOI2019]语言[树链的并、线段树合并]

    题意 题目链接 分析 考虑枚举每个点的答案,最后除以 2 即可. 可以与 \(u\) 构成合法点对 的集合 为所有经过了 \(u\) 的链的并.因为这些链两两有交,根据结论 "树上两条相交的 ...

  6. [Luogu5327][ZJOI2019]语言(树上差分+线段树合并)

    首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案.根据实现不同有下面几种方法.三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路 ...

  7. Luogu5327 ZJOI2019语言(树上差分+线段树合并)

    暴力树剖做法显然,即使做到两个log也不那么优美. 考虑避免树剖做到一个log.那么容易想到树上差分,也即要对每个点统计所有经过他的路径产生的总贡献(显然就是所有这些路径端点所构成的斯坦纳树大小),并 ...

  8. [ZJOI2019]语言——树剖+树上差分+线段树合并

    原题链接戳这儿 SOLUTION 考虑一种非常\(naive\)的统计方法,就是对于每一个点\(u\),我们维护它能到达的点集\(S_u\),最后答案就是\(\frac{\sum\limits_{i= ...

  9. [LOJ3046][ZJOI2019]语言:树链的并+线段树合并

    分析 问题显然可以转化为对于每个节点询问所有这个节点的所有链的链并的大小. 考场上我直接通过树剖打标记+树剖线段树维护以\(O(n \log^3 n)\)的时间复杂度暴力实现了这个过程.(使用LCT或 ...

随机推荐

  1. ngx instance

    首先看下 连接池的获取以及释放 ngx_connection_t * ngx_get_connection(ngx_socket_t s, ngx_log_t *log) //从连接池中获取一个ngx ...

  2. kafka消费者offset存储策略

    由于 consumer 在消费过程中可能会出现断电宕机等故障,consumer 恢复后,需要从故 障前的位置的继续消费,所以 consumer 需要实时记录自己消费到了哪个 offset,以便故障恢 ...

  3. Angualr 内置工具-SelectionModel

    SelectionModel: 被用来控制选中一个和多个item时候的逻辑.例如下拉菜单,复选框选中等,非常方便. 引入:import{SelectionModel}from'@angular/cdk ...

  4. Java 获取微信小程序二维码(可以指定小程序页面 与 动态参数)

    一.准备工作 微信公众平台接口调试工具 小程序的唯一标识(appid) 小程序的密钥(secret) 二.获取access_token 打开微信公众平台接口调试工具,在参数列表中输入小程序的appid ...

  5. OMV openmediavault NAS系统命令显示颜色

    闲鱼65f元买的我家云刷了OMV系统. 但ls命令查看文件不显示颜色. cd /etc/进入配置文件目录查看并没有bashrc文件,但有个bash.bashrc 在 bash.bashrc后面加入以下 ...

  6. CVE-2020-3452 CISCO ASA远程任意文件读取漏洞

    0x01 漏洞描述     Cisco官方 发布了 Cisco ASA 软件和 FTD 软件的 Web 接口存在目录遍历导致任意文件读取 的风险通告,该漏洞编号为 CVE-2020-3452.     ...

  7. 图解HTTP简单笔记【上】

    第一章 了解WEB及网络基础(省略了TCP/IP的知识点) 1.1.使用HTTP协议访问web  当我们在主机的浏览器的地址输入URL之后 请求将回发送至目标服务器 目标服务器在接受到响应请求时将会响 ...

  8. 面试官:别的我不管,这个JVM虚拟机内存模型你必须知道

    前言 说jvm的内存模型前先了解一下物理计算机的内存处理. 物理计算器上用户磁盘和cpu的交互,由于cpu读写速度速度远远大于磁盘的读写速度速度,所以有了内存(高速缓存区).但是随着cpu的发展,内存 ...

  9. C语言模拟实现先来先服务(FCFS)和短作业优先(SJF)调度算法

    说明 该并非实现真正的处理机调度,只是通过算法模拟这两种调度算法的过程. 运行过程如下: 输入进程个数 输入各个进程的到达事件 输入各个进程的要求服务事件 选择一种调度算法 程序给出调度结果:各进程的 ...

  10. xcode6新建pch文件过程

    h1, h2, h3, h4, h5, h6, p, blockquote { margin: 0; padding: 0 } body { font-family: "Helvetica ...