[USACO19JAN]Exercise Route
这题的数据有点水,暴力合并\(set\)好像过了
分析一下这个题的性质,发现我们一条非树边就会形成一个环,而我们要求选择两个非树边,就会形成两个环,要求不走重复的点,就是说我们需要走一个大环,且必须经过这两个小环
显然需要这两个小环有至少一条公共边
发现问题转化成了求有多少对路径,这对路径有公共边
应该可以用数据结构来维护大力讨论的,当然树上差分的正解思路也非常妙
于是我们写个暴力吧!
对于这道题我第一想法是启发式合并\(set\),我们可以利用如同树上差分那样的方式,对于一条路径\((u,v)\),我们在\(u,v\)处打上一个标记,在\(lca(u,v)\)再打上一个删除标记,每次合并\(set\)的时候,往\(set\)里插入的所有标记都表明这个标记代表的路径经过了这条边,这样这条路径就可以和原来\(set\)里的标记任意组合了,就可以统计答案了
但是会和正解树上差分遇到一样的问题,就是如果两条路径的公共边形成的不是一条链,这对路径我们会计算两次
这里采用和正解一样的方法就好了,就是利用一个\(map\)来找到这样的路径有多少对
到这里复杂度还非常正常,是有点大的\(O(nlog^2n)\),但是我们又发现我们好像没有什么办法去利用删除标记
如果每次合并前都要扫一遍删除集合的话,复杂度显然就不对了,菊花树随便卡掉
于是我们启发式删除,看看维护删除集合小还是待合并集合小,那个小就遍历哪一个
还是过不去怎么办,那就把\(set\)换成unordered_set,信仰一下就可以了
代码
#include<tr1/unordered_set>
#include<tr1/unordered_map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 200005
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define set_it unordered_set<int>::iterator
# define getchar() (S==T&&(T=(S=BB)+fread(BB,1,1<<15,stdin),S==T)?EOF:*S++)
using namespace std::tr1;
char BB[1 << 18],*S=BB,*T=BB;
inline int read() {
int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn<<1];
int head[maxn],deep[maxn],sum[maxn],fa[maxn],top[maxn],son[maxn],f[maxn][19],st[maxn];
int n,m,num,cnt,md,t;
int rt[maxn];
LL ans;
unordered_set<int> s[maxn],del[maxn];
unordered_map<LL,int> ma;
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
void dfs1(int x) {
sum[x]=1;int maxx=-1;
for(re int i=head[x];i;i=e[i].nxt) {
if(deep[e[i].v]) continue;
deep[e[i].v]=deep[x]+1;fa[e[i].v]=x;f[e[i].v][0]=x;
for(re int j=1;j<=18;j++) f[e[i].v][j]=f[f[e[i].v][j-1]][j-1];
dfs1(e[i].v);
sum[x]+=sum[e[i].v];
if(sum[e[i].v]>maxx) maxx=sum[e[i].v],son[x]=e[i].v;
}
}
void dfs2(int x,int topf) {
top[x]=topf;
if(!son[x]) return;
dfs2(son[x],topf);
for(re int i=head[x];i;i=e[i].nxt) {
if(top[e[i].v]) continue;
dfs2(e[i].v,e[i].v);
}
}
inline int LCA(int x,int y) {
while(top[x]!=top[y]) {
if(deep[top[x]]<deep[top[y]]) std::swap(x,y);
x=fa[top[x]];
}
if(deep[x]<deep[y]) return x;return y;
}
inline void D(int a,int c) {
if(del[c].size()<s[a].size()) {
for(set_it it=del[c].begin();it!=del[c].end();++it) {
int x=*it;
s[a].erase(x);
}
return;
}
t=0;
for(set_it it=s[a].begin();it!=s[a].end();++it)
if(del[c].find(*it)!=del[c].end()) st[++t]=*it;
while(t) s[a].erase(st[t--]);
}
inline void merge(int a,int b,int c) {
int now=s[a].size();
for(set_it it=s[b].begin();it!=s[b].end();++it) {
int x=*it;
if(del[c].find(x)!=del[c].end()) continue;
ans+=now;s[a].insert(x);
}
}
inline int jump(int x,int y) {
for (re int i=18;i>=0;--i) if(deep[x]-(1<<i)>deep[y]) x=f[x][i];
return x;
}
void dfs(int x) {
D(rt[x],x);
for(re int i=head[x];i;i=e[i].nxt) {
if(deep[e[i].v]<deep[x]) continue;
dfs(e[i].v);
if(s[rt[e[i].v]].size()<=s[rt[x]].size()) {
if(x!=1) merge(rt[x],rt[e[i].v],x);
}
else {
D(rt[e[i].v],x);
if(x!=1) merge(rt[e[i].v],rt[x],x),rt[x]=rt[e[i].v];
}
}
}
int main() {
n=read(),m=read();int x,y,z;
for(re int i=1;i<n;i++) x=read(),y=read(),add(x,y),add(y,x);
deep[1]=1,dfs1(1),dfs2(1,1);
for(re int i=1;i<=n;i++) rt[i]=i;
for(re int i=n;i<=m;i++) {
x=read(),y=read();z=LCA(x,y);
if(x==y) continue;
if(deep[x]<deep[y]) std::swap(x,y);
ans+=s[x].size();
if(z!=y) ans+=s[y].size();
s[x].insert(i);
if(z!=y) s[y].insert(i);
del[z].insert(i);
if(x==z||y==z) continue;
int xx=jump(x,z),yy=jump(y,z);
if(xx>yy) std::swap(xx,yy);
ans-=ma[(LL)xx*(LL)n+yy];ma[(LL)xx*(LL)n+yy]++;
}
dfs(1);
printf("%lld\n",ans);
}
[USACO19JAN]Exercise Route的更多相关文章
- [USACO19JAN]Exercise Route P
先让我们探索一下两条非树边以及树边能构成简单环的条件是什么,你会发现将第一条非树边的两个点在树上形成的链记为 \(W_1\),另一条即为 \(W_2\),那么当且仅当 \(W_1, W_2\) 有交时 ...
- [USACO18DEC]The Cow Gathering P
首先可以思考一下每次能删去的点有什么性质. 不难发现,每次能删去的点都是入度恰好为 \(1\) 的那些点(包括 \(a_i \rightarrow b_i\) 的有向边). 换句话说,每次能删去的点既 ...
- Application Request Route实现IIS Server Farms集群负载详解
序言 随着公司业务的发展,后台业务就变的越来越多,然而服务器的故障又像月经一样,时不时的汹涌而至,让我们防不胜防.那么后台的高可用,以及服务器的处理能力就要做一个横向扩展的方案,以使后台业务持续的稳定 ...
- .net core 源码解析-mvc route的注册,激活,调用流程(三)
.net core mvc route的注册,激活,调用流程 mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活c ...
- angular路由——ui.route
angular路由 使用案例 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- 如何在ARM中创建Express Route
很早之前就想试试Azure的express route,但是一直没有找到合适的机会,正好有个客户需要上express route,所以最近先自己研究研究,防止在做poc的时候耗费更多时间,本次场景我们 ...
- Python flask @app.route
转载自 http://python.jobbole.com/80956/ 下面是Flask主页给我们的第一个例子,我们现在就由它入手,深入理解“@app.route()”是如何工作的. ...
- MIT 6.828 JOS学习笔记12 Exercise 1.9
Lab 1中Exercise 9的解答报告 Exercise 1.9: 判断一下操作系统内核是从哪条指令开始初始化它的堆栈空间的,以及这个堆栈坐落在内存的哪个地方?内核是如何给它的堆栈保留一块内存空间 ...
- MIT 6.828 JOS学习笔记13 Exercise 1.10
Lab 1 Exercise 10 为了能够更好的了解在x86上的C程序调用过程的细节,我们首先找到在obj/kern/kern.asm中test_backtrace子程序的地址, 设置断点,并且探讨 ...
随机推荐
- Eclipse 工具栏无法移动的解决办法
升级到Juno后发现工具栏有些乱 而且无法拖动,试了下http://blog.csdn.net/cxx504659987/article/details/38532599的方法 发现配置文件里没有文中 ...
- cocos-creator 脚本逻辑-1
1.节点 编辑组件的脚本文件时.可以通过以下语句获得节点 this 就是当前组件 this.node 拿到组件依附的节点 This.node.parent 拿到组件依附的节点 的父节点 This.no ...
- 拖动条SeekBar
1TextView tv=(TextView)findViewById(R.id.TV); 2 tv.setMovementMethod(ScrollingMovementMethod.getInst ...
- mac下打开hosts文件
1打开控制台 输入vi(空格)/etc/hosts 进入hosts文件,输入i更改为编辑状态,更改完esc然后shift+:在输入wq保存退出 2打开Finder然后选择上面前往,到前往文件夹,输入/ ...
- 鼠标移动事件(跟随鼠标移动的div)
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- 08_zookeeper的客户端工具curator_watcher
[使用usingWatcher()] //两个usingWatcher的传入参数不同 public interface Watchable<T> { T watched(); T usin ...
- 【ArcGIS】最近遇到的几个已经被解决的问题
昨天刚出差回来,自己的第一个地图项目也就快接近尾声了,回到公司马上就打开了博客园记录一下最近遇到的几个地图相关的问题. 1.在ArcGIS server上点击 View In:ArcGIS J ...
- 【Machine Learning】分类与回归 区别
一.分类与回归的区别 两类监督学习 Classification Regression 分类和回归的区别在于输出变量的类型(而非输入变量). 定性输出称为分类,或者说是离散变量预测(discrete) ...
- J2EE项目工具集(转)
1.支持重构,TDD, Debug J2EE应用和Flying Error提示的IDE a.重构:即使团队用的最多的只是Rename,Move,Extract Method等有限几个最基本的功能,但J ...
- css display属性详解
css display属性在对css做layout设计时非常重要,它的值有以下几种: Value Description Play it inline Default value. Displays ...