SADPAIRS

删点不连通,点双,圆方树

非割点:没有影响

割点:子树DP一下

有不同颜色,所以建立虚树

在圆方树上dfs时候

如果当前点是割点

1.统计当前颜色虚树上的不连通点对,树形DP即可

2.统计所有颜色的虚树上的不连通点对。。。。

一个麻烦事是,虚树上一条边上选择一个原树割点,都会对这个虚树造成相同的影响(两边sz乘积)

n,m 2e5

树上差分

设虚树上,(x,y)的边,x是y的父亲

原树上,x的位置减去贡献,y的原树father位置加上贡献

最后dfs扫一遍就行了。

实际上麻烦事挺多:

1.不保证连通,所以圆方树森林,虚树森林

2.LCA可能是多个颜色虚树的节点,dp[x]是累计的

3.x断线只有的ans:原来本身不连通的+经过x的+x和同种别的颜色 (后面两个都是自己连通块内部)

错点:

1.dfn的cmp没有写进sort函数

2.vis在dfs时候没有赋值导致循环多遍

3.存在孤立点,不在任意一个DCC中,赋值typ=1的时候,特殊考虑到(其实typ=1没有意义)

4.统计的是当前连通块当前颜色的数量,所以初值:sz[x]=(co[x]==now)

5.答案爆int。两块sz相乘也会爆int

代码:

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=4e5+;
int n,m;
int a[N][];
int kind;//species
struct node{
int nxt,to;
}e[*N];
int hd[N],cnt;
void add(int x,int y){
e[++cnt].nxt=hd[x];
e[cnt].to=y;
hd[x]=cnt;
}
int dfn[N],low[N],df,dfn2[N];
int co[N],b[N],dcc;
vector<int>mem[N];
ll preno;
vector<int>be[N];
int typ[N];//1:yuan 0:fang
int sta[N],top;
void tarjan(int x){
dfn[x]=low[x]=++df;
sta[++top]=x;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
++dcc;
int z;
do{
z=sta[top];
mem[dcc].push_back(z);
--top;
}while(z!=y);
mem[dcc].push_back(x);
}
}else low[x]=min(low[x],dfn[y]);
}
}
int belong[N];
int dep[N],fa[N][];
void dfs1(int x,int bl,int d){
belong[x]=bl;
dfn[x]=++df;
dep[x]=d;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(!dfn[y]){
fa[y][]=x;
dfs1(y,bl,d+);
}
}
dfn2[x]=df;
}
ll dp[N];
ll tag[N];
int vis[N];
ll sz[N];
void finsz(int x,int now){
vis[x]=now+kind;
sz[x]=(co[x]==now);
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
finsz(y,now);
sz[x]+=sz[y];
}
}
void dfs2(int x,ll totsz,int now){
// cout<<" dfs2 "<<x<<" : "<<totsz<<" "<<now<<endl;
ll presz=;
//dp[x]=0;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
dfs2(y,totsz,now);
tag[x]-=(totsz-sz[y])*sz[y];
//cout<<" fa[y][0] "<<fa[y][0]<<endl;
tag[fa[y][]]+=(totsz-sz[y])*sz[y];
dp[x]+=presz*sz[y];
presz+=sz[y];
}
dp[x]+=presz*(totsz-sz[x]);
if(co[x]==now)dp[x]+=totsz-typ[x];
}
ll ans[N]; void sol(int x,int fa){
vis[x]=;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
sol(y,x);
tag[x]+=tag[y];
}
//cout<<" x "<<x<<" : "<<preno+dp[x]+tag[x]<<endl;
if(typ[x]) ans[x]=preno+dp[x]+tag[x];
}
bool cmp(int x,int y){
return dfn[x]<dfn[y];
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(reg j=;j>=;--j){
if(dep[fa[x][j]]>=dep[y]) x=fa[x][j];
}
if(x==y) return x;
for(reg j=;j>=;--j){
if(fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
}
return fa[x][];
}
int main(){
rd(n);rd(m);
int tot=;
for(reg i=;i<=n;++i) rd(co[i]),b[++tot]=co[i];
sort(b+,b+tot+);
tot=unique(b+,b+tot+)-b-;
kind=tot;
for(reg i=;i<=n;++i){
typ[i]=; co[i]=lower_bound(b+,b+tot+,co[i])-b;
//cout<<co[i]<<" ";
be[co[i]].push_back(i);
}
// cout<<endl; int x,y;
for(reg i=;i<=m;++i){
rd(x);rd(y);add(x,y);add(y,x);
} for(reg i=;i<=n;++i){
if(!dfn[i]) tarjan(i);
}
memset(hd,,sizeof hd);
memset(dfn,,sizeof dfn);
df=;cnt=;
tot=n;
int edge=;
//cout<<" dcc "<<dcc<<endl;
for(reg i=;i<=dcc;++i){
++tot;
typ[tot]=;
//cout<<"num ******************"<<tot<<endl;
for(reg j=;j<(int)mem[i].size();++j){
// cout<<mem[i][j]<<" ";
typ[mem[i][j]]=;
a[++edge][]=tot;
a[edge][]=mem[i][j];
add(tot,mem[i][j]);
add(mem[i][j],tot);
}
//cout<<endl;
}
// cout<<" tot "<<tot<<endl;
int kuai=;
for(reg i=;i<=tot;++i){
if(!dfn[i]){
dfs1(i,++kuai,);
}
}
// cout<<" kuai "<<kuai<<endl;
memset(hd,,sizeof hd);
cnt=; for(reg j=;j<=;++j){
for(reg i=;i<=n;++i){
fa[i][j]=fa[fa[i][j-]][j-];
}
}
for(reg i=;i<=kind;++i){
//cout<<" kind i "<<i<<" ------------------------------- "<<endl;
int num=be[i].size();
sort(be[i].begin(),be[i].end(),cmp);
for(reg j=;j<num;++j){
vis[be[i][j]]=vis[be[i][j-]]=i;
if(belong[be[i][j]]==belong[be[i][j-]]){
int anc=lca(be[i][j],be[i][j-]);
if(vis[anc]!=i){
vis[anc]=i;
be[i].push_back(anc);
}
}
}
sort(be[i].begin(),be[i].end(),cmp);
top=; for(reg j=;j<(int)be[i].size();++j){
// cout<<" j "<<be[i][j]<<endl;
while(top&&(!(dfn[sta[top]]<=dfn[be[i][j]]&&dfn[be[i][j]]<=dfn2[sta[top]])))--top;
if(top) add(sta[top],be[i][j]);
sta[++top]=be[i][j];
} ll has=;
for(reg j=;j<(int)be[i].size();++j){
if(vis[be[i][j]]!=i+kind){
finsz(be[i][j],i);
// cout<<" totsz "<<be[i][j]<<" "<<typ[be[i][j]]<<" "<<sz[be[i][j]]<<endl;
dfs2(be[i][j],sz[be[i][j]],i);
preno+=has*sz[be[i][j]];
has+=sz[be[i][j]];
}
} for(reg j=;j<(int)be[i].size();++j){
hd[be[i][j]]=;
sz[be[i][j]]=;
}
cnt=;
}
// cout<<" preno "<<preno<<endl;
// for(reg i=1;i<=tot;++i){
// printf("%d dp %lld %lld\n",i,dp[i],tag[i]);
// }
memset(vis,,sizeof vis);
for(reg i=;i<=edge;++i){
add(a[i][],a[i][]);
add(a[i][],a[i][]);
}
for(reg i=;i<=tot;++i){
if(!vis[i]){
sol(i,);
}
}
for(reg i=;i<=n;++i){
printf("%lld\n",ans[i]);
}
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2019/2/19 17:48:21
*/

Codechef Sad Pairs——圆方树+虚树+树上差分的更多相关文章

  1. 仙人掌 && 圆方树 && 虚树 总结

    仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...

  2. [SDOI2018]战略游戏(圆方树+虚树)

    喜闻乐见的圆方树+虚树 图上不好做,先建出圆方树. 然后答案就是没被选到的且至少有两条边可以走到被选中的点的圆点的数量. 语文不好,但结论画画图即可得出. 然后套路建出虚树. 发现在虚树上DP可以得出 ...

  3. BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)

    Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着 ...

  4. BZOJ.5329.[SDOI2018]战略游戏(圆方树 虚树)

    题目链接 显然先建圆方树,方点权值为0圆点权值为1,两点间的答案就是路径权值和减去起点终点. 对于询问,显然可以建虚树.但是只需要计算两关键点间路径权值,所以不需要建出虚树.统计DFS序相邻的两关键点 ...

  5. UOJ.87.mx的仙人掌(圆方树 虚树)(未AC)

    题目链接 本代码10分(感觉速度还行..). 建圆方树,预处理一些东西.对询问建虚树. 对于虚树上的圆点直接做:对于方点特判,枚举其所有儿子,如果子节点不在该方点代表的环中,跳到那个点并更新其val, ...

  6. 洛谷P4606 [SDOI2018]战略游戏 【圆方树 + 虚树】

    题目链接 洛谷P4606 双倍经验:弱化版 题解 两点之间必经的点就是圆方树上两点之间的圆点 所以只需建出圆方树 每次询问建出虚树,统计一下虚树边上有多少圆点即可 还要讨论一下经不经过根\(1\)的情 ...

  7. P4606-[SDOI2018]战略游戏【圆方树,虚树】

    正题 题目链接:https://www.luogu.com.cn/problem/P4606 题目大意 给出\(n\)个点\(m\)条边的一张图,\(q\)次询问给出一个点集,询问有多少个点割掉后可以 ...

  8. 洛谷4606 SDOI2018战略游戏(圆方树+虚树)

    QWQ深受其害 当时在现场是真的绝望...... 现在再重新来看这个题 QWQ 根据题目所说,我们可以发现,对于每一个集合中的节点,我们实际上就是要求两两路径上的割点的数目 考虑到又是关于点双的题目, ...

  9. Luogu P4606 [SDOI2018] 战略游戏 圆方树 虚树

    https://www.luogu.org/problemnew/show/P4606 把原来的图的点双联通分量缩点(每个双联通分量建一个点,每个割点再建一个点)(用符合逻辑的方式)建一棵树(我最开始 ...

随机推荐

  1. web窗体的运用

    using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace WebAp ...

  2. 利用OVS+FLOODLIGHT,为数据表添加VLAN_ID和MPLS

    话不多说,直接上拓扑: 我这里是用主机h1 (10.0.0.1)ping 主机h2(10.0.0.2) 1.添加VLAN标签 v1: sudo ovs-ofctl add-flow m1-s1 in_ ...

  3. [LOJ#6033]. 「雅礼集训 2017 Day2」棋盘游戏[二分图博弈、匈牙利算法]

    题意 题目链接 分析 二分图博弈经典模型,首先将棋盘二分图染色. 考虑在某个最大匹配中: 如果存在完美匹配则先手必败,因为先手选定的任何一个起点都在完美匹配中,而后手则只需要走这个点的匹配点,然后先手 ...

  4. .NET Core容器化开发系列(零)——计划

    .NET Core相当完善的跨平台特性以及其轻量化的底层接口为我们能顺畅进行微服务开发提供了非常棒的基础. 作为支撑微服务最常见的基础技术--容器化将是本系列的核心内容. 接下来我计划用一个月左右的时 ...

  5. 高精度加法--C++

    高精度加法--C++ 仿照竖式加法,在第一步计算的时候将进位保留,第一步计算完再处理进位.(见代码注释) 和乘法是类似的. #include <iostream> #include < ...

  6. RPG游戏开发基础教程

    RPG游戏开发基础教程 第一步 下载RPG Maker 开发工具包 1.RPG Maker 是什么? RPG Maker 是由Enterbrain公司推出的RPG制作工具. 中文译名为RPG制作大师. ...

  7. 我用Python爬虫挣钱的那些事

    在下写了10年Python,期间写了各种奇葩爬虫,挣各种奇葩的钱,写这篇文章总结下几种爬虫挣钱的方式. 1.最典型的就是找爬虫外包活儿. 这个真是体力活,最早是在国外各个freelancer网站上找适 ...

  8. 501. Find Mode in Binary Search Tree【LeetCode by java】

    Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred ...

  9. 毕业回馈-89c52之最小系统

    今天分享一个51单片机最小系统的电路板设计(原理图+PCB) 技术手册上面对于51单片机最小系统作如下要求: 下载电路主要有以下几种: 采用RS-232转换器下载:(R1OUT-P3.0;T1IN-P ...

  10. 【Alpha】第四次Scrum meeting

    今天任务一览: 姓名 今日完成任务 所耗时间 刘乾 配置好了所有物理实验的通配模板,为服务器配置了latex中文环境,设置了一些常用字体. Issue链接:https://github.com/bua ...