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. 20155237方自晨 实验四android开发基础

    提交点一 Android Stuidio的安装测试: 参考<Java和Android开发学习指南(第二版)(EPUBIT,Java for Android 2nd)>第二十四章: 安装 A ...

  2. Page结构

    SQL Server存储数据的基本单元是Page,每一个Page的大小是8KB,数据文件是由Page构成的.在同一个数据库上,每一个Page都有一个唯一的资源标识,标识符由三部分组成:db_id,fi ...

  3. JavaScript快速入门-简介

    一.JavaScript历史(摘自w3school) JavaScript 是因特网上最流行的脚本语言,它存在于全世界所有 Web 浏览器中,能够增强用户与 Web 站点和 Web 应用程序之间的交互 ...

  4. node基础:文件系统-文件读取

    node的文件读取主要分为同步读取.异步读取,常用API有fs.readFile.fs.readFileSync.还有诸如更底层的fs.read,以及数据流(stream),后面再总结下咯~ 直接上简 ...

  5. Mvc4_语法基础介绍

    @model MvcApplicationTest.Models.User @{ ViewBag.Title = "Index"; } <script type=" ...

  6. 吴恩达《AI For Everyone》_练习英语翻译_待更新

    AI For Everyone https://www.coursera.org/learn/ai-for-everyone 讲师: Andrew Ng (吴恩达) CEO/Founder Landi ...

  7. 如何解决python连接数据库编码问题(python传数据到mysql乱码)'ascii' codec can't encode _mysql_exceptions.OperationalError: (1366, "Incorrect string value:?

    首先描述下问题:  在使用python计算出结果后将结果插入到mysql过程中,报如下错误.原因很好定位就是编码的问题.那么到底是编码哪里出了问题了呢? 报错如上: 排查顺序: 第一:python的编 ...

  8. Mocha 单元测试框架简介

    前言: mocha是JavaScript的一种单元测试框架,既可以在浏览器环境下运行,也可以在Node.js环境下运行. 使用mocha,我们就只需要专注于编写单元测试本身,然后,让mocha去自动运 ...

  9. Final版本互评——杨老师粉丝群《PinBall》

    基于NABCD评论作品,及改进建议 1.根据(不限于)NABCD评论作品的选题 (1)N(Need,需求) 随着民族自信的觉醒,民主文化越来越受到重视,语文在高考中的比重也不断增加,在这种大环境下,成 ...

  10. Notes of Daily Scrum Meeting(12.3)

    这个月大家的课业任务很重啊,加上软工有三个课程设计要完成了,感觉亚历山大的说,而且我们alpha阶段完成度低一些, 所以任务更多了,今天做的东西就不多,希望大家加油吧! 团队成员 今日团队工作 陈少杰 ...