题目链接

dalao们怎么都写的线段树合并啊。。

dsu跑的好慢。


\(Description\)

给定一棵\(n(n\leq 10^5)\)个点的树。

定义\(Tree[L,R]\)表示为了使得\(L\sim R\)号点两两连通,最少需要选择的边的数量。

求$$\sum_{l=1}n\sum_{r=l}nTree[l,r]$$

\(Solution\)

枚举每条边,计算它的贡献。

那么我们要判断有多少连续区间的点跨过这条边,并不好算,反过来去求在这条边的两侧分别有多少个连续区间。

那么显然有\(O(n^2)\)的做法,即对每条边DFS它的两侧,枚举一下每一侧的连续区间。

我们还可以DFS这棵树,对于每个点我们需要计算它子树内和子树外的连续区间数。

对于子树内的点,并查集显然是可以维护的(每次合并相邻点成为一个连续区间时新产生的连续区间数可算,就是两个集合\(size\)的乘积)。

对于子树外的点怎么算呢。我们可以先假设子树外为\(1,2,...,n\),且有这些区间。我们每次在子树中加入点时,就把它从子树外的集合的点中删掉,并计算子树外少的区间数。

我们发现不需要\(n\)个元素的集合,也不需要这样删。在一个初始只有\(0\)和\(n+1\)的空集合里,每次加入子树内的点,然后计算,也是等价的。

于是我们需要对每个子树合并并查集及处理set。

可以用dsu on tree,每次先处理完轻儿子的子树,每次处理完都清空那棵子树的贡献/状态(并查集、set)。最后处理重儿子所在子树,并保留其状态。

处理完这整棵子树后(也就是算完该边答案后),再把子树内其它未加入的轻子树给加入并查集、set。

dsu的复杂度是\(O(n\log n)\)的,再套上别的,复杂度为\(O(n\log^2n)\)。


#include <set>
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 150000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define Calc(x) (1ll*(x)*(x-1)>>1ll)//区间个数
typedef long long LL;
const int N=1e5+5; int n,Enum,H[N],nxt[N<<1],to[N<<1],fa[N],sz[N],son[N],Fa[N],size[N];
bool vis[N];//计算子树内的连续区间(是否子树内已存在相邻的)
LL Ans,sum1,sum2;
std::set<int> st;
char IN[MAXIN],*SS=IN,*TT=IN; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DFS1(int x)
{
int mx=0; sz[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x])
{
fa[v]=x, DFS1(v), sz[x]+=sz[v];
if(sz[v]>mx) mx=sz[v], son[x]=v;
}
}
int Find(int x)
{
return x==Fa[x]?x:Fa[x]=Find(Fa[x]);
}
void Upd(int x)
{
st.insert(x);//我怎么记得有返回值(iterator)来...
std::set<int>::iterator it=st.find(x),pre=it,nxt=++it;
--pre;
sum2-=Calc(*nxt-*pre-1);//子树外以前的连续区间被分割
sum2+=Calc(x-*pre-1)+Calc(*nxt-x-1); vis[x]=1;
if(vis[x-1])
{
int r1=Find(x-1),r2=Find(x);//它们之前显然不会在一个集合啊(它们之间只会算一次,要么是加x-1,以前有x;要么是加x,以前有x-1)。
sum1+=1ll*size[r1]*size[r2];
Fa[r1]=r2, size[r2]+=size[r1];
}
if(vis[x+1])
{
int r1=Find(x+1),r2=Find(x);
sum1+=1ll*size[r1]*size[r2];
Fa[r1]=r2, size[r2]+=size[r1];
}
}
void Clear(int x)
{
Fa[x]=x, size[x]=1, vis[x]=0;
for(int i=H[x]; i; i=nxt[i])
if(to[i]!=fa[x]) Clear(to[i]);
}
void Update(int x)
{
Upd(x);
for(int i=H[x]; i; i=nxt[i])
if(to[i]!=fa[x]) Update(to[i]);
}
void DFS2(int x)
{
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x]&&v!=son[x])
{
DFS2(v), Clear(v);
st.clear(), st.insert(0), st.insert(n+1);
sum1=0, sum2=Calc(n);//还是在DFS完子树后就初始化吧
}
if(son[x]) DFS2(son[x]); for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x]&&v!=son[x]) Update(v); Upd(x);
Ans+=Calc(n)-sum1-sum2;
} int main()
{
freopen("treecnt.in","r",stdin);
freopen("treecnt.out","w",stdout); n=read();
for(int i=1; i<n; ++i) AE(read(),read());
for(int i=1; i<=n; ++i) Fa[i]=i,size[i]=1;//!
st.insert(0), st.insert(n+1);//边界.
sum1=0, sum2=Calc(n), DFS1(1), DFS2(1), printf("%lld\n",Ans); return 0;
}

某一种\(O(n^2)\)做法:(见Subtask1()

写了个双链表。。心疼zz的自己。。

#include <set>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5,INF=0x3f3f3f3f; int dgr[N],Enum,H[N],nxt[N<<1],to[N<<1],fa[N],dep[N],sz[N],son[N],top[N],dfn[N],ref[N];
char IN[MAXIN],*SS=IN,*TT=IN; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AE(int u,int v)
{
// ++dgr[u], ++dgr[v];
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
inline int LCA(int u,int v)
{
// printf("LCA(%d,%d)\n",u,v);
while(top[u]!=top[v]) dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
return dep[u]>dep[v]?v:u;
}
inline int Dis(int u,int v)
{
// printf("(%d,%d) Subd:dep[%d]=%d!\n",u,v,LCA(u,v),dep[LCA(u,v)]);
return dep[u]+dep[v]-(dep[LCA(u,v)]<<1);
}
void DFS1(int x)
{
int mx=0; sz[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x])
{
fa[v]=x, dep[v]=dep[x]+1, DFS1(v), sz[x]+=sz[v];
if(sz[v]>mx) mx=sz[v], son[x]=v;
}
}
void DFS2(int x,int tp)
{
static int Index=0; top[x]=tp, dfn[x]=++Index;
if(son[x])
{
DFS2(son[x],tp);
for(int i=H[x]; i; i=nxt[i])
if(to[i]!=fa[x]&&to[i]!=son[x]) DFS2(to[i],to[i]);
}
}
inline bool cmp_dfn(int i,int j)
{
return dfn[i]<dfn[j];
}
void Output(int *l,int *r,int n)
{
printf("\nOutput the list:\n%d",0);
for(int p=r[0]; p!=n+1; p=r[p]) printf("->%d(%d)",p,l[p]); puts("\n");
}
void Subtask0(int n)
{
const int N=305;
static int A[N]; LL ans=0;
for(int i=1; i<=n; ++i)
{
int t=1; A[1]=i;
for(int j=i+1; j<=n; ++j)
{
A[++t]=j, std::sort(A+1,A+1+t,cmp_dfn);
for(int k=1; k<t; ++k) ans+=Dis(A[k],A[k+1]);
ans+=Dis(A[t],A[1]);
}
}
printf("%I64d\n",ans>>1ll);
}
void Subtask1(int n)
{
const int N=3005;
static int A[N],SL[N],SR[N],L[N],R[N],id[N],tl[N],tr[N]; LL ans=0;
for(int i=1; i<=n; ++i) ans+=1ll*(1ll*(i-1)*(n-i+1)+n-i)*dep[i],id[i]=i;
// printf("pre_ans=%I64d\n\n",ans); std::sort(id+1,id+1+n,cmp_dfn);
SR[0]=id[1], SL[n+1]=id[n], id[n+1]=n+1;
for(int i=1; i<=n; ++i) SL[id[i]]=id[i-1], SR[id[i]]=id[i+1]; // for(int i=1; i<=n; ++i) printf("dfn[%d]=%d\n",i,dfn[i]); puts("");
// Output(SL,SR,n); for(int i=1; i<n; ++i)
{
memset(tl,0,sizeof tl), memset(tr,0,sizeof tr);
memcpy(L,SL,sizeof SL), memcpy(R,SR,sizeof SR);
// Output(L,R,n); for(int j=n,T=1,l,r; j>i; --j,++T)
{
int a,b;
if((l=L[j])==0) a=j, b=L[n+1];
else a=j, b=l;
int tmp=std::min(T-tr[b],T-tl[a]);
ans-=1ll*tmp*dep[LCA(a,b)], tr[b]=T; if((r=R[j])==n+1) b=j, a=R[0];
else b=j, a=r;
tmp=std::min(T-tr[b],T-tl[a]);
ans-=1ll*tmp*dep[LCA(a,b)], tl[a]=T; R[l]=r, L[r]=l; // if((l=L[j])==0) ans-=dep[LCA(j,L[n+1])], printf("Subd:dep[%d]=%d!\n",LCA(j,L[n+1]),dep[LCA(j,L[n+1])]);
// else ans-=dep[LCA(j,l)], printf("Subd:dep[%d]=%d!\n",LCA(j,l),dep[LCA(j,l)]);
// if((r=R[j])==n+1) ans-=dep[LCA(j,R[0])], printf("Subd:dep[%d]=%d!\n",LCA(j,R[0]),dep[LCA(j,R[0])]);
// else ans-=dep[LCA(j,r)], printf("Subd:dep[%d]=%d!\n",LCA(j,r),dep[LCA(j,r)]);
// R[l]=r, L[r]=l;
}
SR[SL[i]]=SR[i], SL[SR[i]]=SL[i];
}
printf("%I64d\n",ans);
}
namespace Subtask2
{
// struct BIT
// {
// #define lb(x) (x&-x)
// int n,mn[N],mx[N];
//
// void Init(int nn) {n=nn; memset(mn,0x3f,sizeof mn), memset(mx,0,sizeof mx);}
// void Modify_Max(int p,int v)
// {
// for(; p<=n; p+=lb(p)) mx[p]=std::max(mx[p],v);
// }
// void Modify_Min(int p,int v)
// {
// for(; p<=n; p+=lb(p)) mn[p]=std::min(mn[p],v);
// }
// int Query_Max(int p)
// {
// int res=0;
// for(; p; p^=lb(p)) res=std::max(res,mx[p]);
// return res;
// }
// int Query_Min(int p)
// {
// int res=INF;
// for(; p; p^=lb(p)) res=std::min(res,mn[p]);
// return res;
// }
// }Tp,Ts;
// int pos[N],ref[N];
//
// void DFS3(int x,int f,int t)
// {
//// pos[x]=t, ref[t]=x;
// pos[t]=x;
// for(int i=H[x]; i; i=nxt[i])
// if(to[i]!=f) DFS3(to[i],x,t+1);
// }
// bool Main(int n)
// {
// for(int i=1; i<=n; ++i) if(dgr[i]>2) return 0;
// for(int i=1; i<=n; ++i) if(dgr[i]==1) {DFS3(i,i,1); break;}
//
// LL ans=0;
// Tp.Init(n), Ts.Init(n);
// for(int i=n; i; --i)
// {
// int p=pos[i],l=Tp.Query_Max(p)+1,r=Ts.Query_Min(n-p+1)-1;
//// if(l==0+1) l=1;
// if(r==INF-1) r=n;
// ans+=1ll*i*(p-l+1)*(r-p+1);
// Tp.Modify_Max(p,p), Ts.Modify_Min(n-p+1,p);
// }
//
// Tp.Init(n), Ts.Init(n);
// for(int i=1; i<=n; ++i)
// {
// int p=pos[i],l=Tp.Query_Max(p)+1,r=Ts.Query_Min(n-p+1)-1;
//// if(l==0+1) l=1;
// if(r==INF-1) r=n;
// ans-=1ll*i*(p-l+1)*(r-p+1);
// Tp.Modify_Max(p,p), Ts.Modify_Min(n-p+1,p);
// }
// printf("%I64d\n",ans);
// return 1;
// }
} int main()
{
freopen("treecnt.in","r",stdin);
freopen("treecnt.out","w",stdout); int n=read();
for(int i=1; i<n; ++i) AE(read(),read()); // if(n>3000 && Subtask2::Main(n)) return 0; DFS1(1), DFS2(1,1);
// Subtask0(n); puts("");
if(n<=300) Subtask0(n);
else if(n<=3000||1) Subtask1(n); return 0;
}

树上统计treecnt(dsu on tree 并查集 正难则反)的更多相关文章

  1. [洛谷U40581]树上统计treecnt

    [洛谷U40581]树上统计treecnt 题目大意: 给定一棵\(n(n\le10^5)\)个点的树. 定义\(Tree[l,r]\)表示为了使得\(l\sim r\)号点两两连通,最少需要选择的边 ...

  2. CF109 C. Lucky Tree 并查集

    Petya loves lucky numbers. We all know that lucky numbers are the positive integers whose decimal re ...

  3. P1197 [JSOI2008]星球大战(并查集判断连通块+正难则反)

    P1197 [JSOI2008]星球大战(并查集判断连通块+正难则反) 并查集本来就是连一对不同父亲的节点就的话连通块就少一个. 题目描述 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统 ...

  4. Is It A Tree?(并查集)(dfs也可以解决)

    Is It A Tree? Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submi ...

  5. Hdu.1325.Is It A Tree?(并查集)

    Is It A Tree? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) To ...

  6. Is It A Tree?(并查集)

    Is It A Tree? Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 26002   Accepted: 8879 De ...

  7. HDU 5606 tree 并查集

    tree 把每条边权是1的边断开,发现每个点离他最近的点个数就是他所在的连通块大小. 开一个并查集,每次读到边权是0的边就合并.最后Ans​i​​=size[findset(i)],size表示每个并 ...

  8. [Swust OJ 856]--Huge Tree(并查集)

    题目链接:http://acm.swust.edu.cn/problem/856/ Time limit(ms): 1000 Memory limit(kb): 10000 Description T ...

  9. Codeforces Round #363 (Div. 2)D. Fix a Tree(并查集)

    D. Fix a Tree time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

随机推荐

  1. mysql caching_sha2_password异常分析

    使用navicat连接mysql报错 解决办法: 通过命令行登录mysql后,输入: alter user 'root'@'localhost' IDENTIFIED WITH mysql_nativ ...

  2. Account的简单架构

    前几天,有园友私下问我,博客中的AccountDemo后端架构为什么是那样的,是不是分层太多太冗余,故这里简单介绍下.先看解决方案工程截图: 每个工程的含义,见https://www.cnblogs. ...

  3. StackOverflowError的原因

    package chapter04; /** 如果两个方法出现互相调用的时候会出现StackOverflowError*/ public class C06_Method { public stati ...

  4. 关于asp.net mvc中的httpModules 与 httpHandler

    ASP.NET对请求处理的过程: 当请求一个*.aspx文件的时候,这个请求会被inetinfo.exe进程截获,它判断文件的后缀(aspx)之后,将这个请求转交给ASPNET_ISAPI.dll,A ...

  5. Pycharm里面使用PIL库之后,为什么调用Image的方法不能弹出代码提示,怎样能让代码提示弹出?

    之前也碰到了这个问题,安装了pillow后没有代码提示,最后查了半天,发现问题原来非常简单,解决方法也很无厘头. 之所以没有代码提示,仅仅是因为Pycharm没法判断Image.open()返回的对象 ...

  6. Zookeeper笔记(三)部署与启动Zookeeper

    下载zookeeper安装包 去Zookeeper官网,下载地址http://zookeeper.apache.org/releases.html,建议下载稳定版本,我下载的是zookeeper-3. ...

  7. [转]svn检出的时候报 Unable to connect to a repository at URL错误

    昨天晚上遇到的问题: 在同一个SVN地址下,很多子目录,各种目录各种权限,现在因为业务,需要下载各种目录下的文件. 第一次遇到这个问题..现象是: 1)在web浏览器下可用: 2)在本地创建的目录下c ...

  8. BZOJ1177 [Apio2009]Oil 二维前缀和 二维前缀最值

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1177 题意概括 在一个n*m的矩阵中,每一个位置一个数字. 现在让你选出3个k*k的矩阵,它们互不 ...

  9. BZOJ1087 [SCOI2005]互不侵犯King 状态压缩动态规划

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1087 题意概括 在n*n的棋盘上面放k个国王,使得他们互相无法攻击,问有多少种摆法. 题解 dp[ ...

  10. 094实战 关于js SDK的程序,java SDK的程序

    一:JS SDK 1.修改配置workspace 2.导入 3.Demo.html <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Trans ...