牛客网 小睿睿的方案 解题报告

小睿睿的方案

链接:

https://ac.nowcoder.com/acm/contest/371/C

来源:牛客网

题目描述

小睿睿虽然已经是人生赢家了,但当他看见学校里其他人秀恩爱时仍旧会十分不满。他的学校有\(n\)个教室,教室间有\(n-1\)条路径且任意教室互相联通,每天他和小熙熙会选择在两个不同的教室\((i,j)\)间的简单路径上秀恩爱。学校里一共有\(m\)对情侣,每对情侣中的两个人分别在教室\(A,B(A\not=B)\),如果他们秀恩爱时经过的教室里存在任意一对情侣,这种秀恩爱的方案就是不合法的,求合法的无序点对数

无序点对:\((i,j)\)\((j,i)\)视作同一对

输入描述:

\(1\)行,\(2\)个整数\(n,m\)

\(2\)\(n\)行,每行两个整数\(a,b\),表示\(a,b\)间存在一条边

之后\(m\)行,每行两个整数\(a,b\),表示有一对情侣分别在教室\(a\)和教室\(b\)

输出描述:

一行一个整数,表示答案

说明

对于\(30\%\)的数据,\(n,m\le 100\)

另有\(20\%\)的数据,\(n,m\le 100000\)且图的形态为一条链

对于\(100\%\)的数据,\(n,m\le 100000\)

输入数据较大,建议使用较快的读入方式


看了前两个题,发现很水,就来写这个题,然后最后5分钟的时候写出来了,结果数组还开小了,爆的只有80分...前面两个题还没做qaq

统计不合法的点对

首先搞一个点分。

然后考虑遍历一颗儿子的子树时,可能激活前面的儿子的一个子树,把这个激活放到线段树上就是区间覆盖,然后处理一个区间覆盖和区间查询1的个数,但是从点退回父亲的时候需要撤回,我没找到别的方法,只好硬肛了一个可持久化上去。

然后还有一个细节,就是当前子树点到分治中心的路径上有一对,这个可以激活之前的全部点,相应的,之前的子树也可能包含这样的链,就差不多处理就好了。

复杂度\(O(n\log^2 n)\)

\(O(n\log n)\)正解

这个题感觉好像是个套路,和接水果那题有点像

就是 处理树上路径包含统计数量之类的题目可以用\(dfs\)搞成二维的,然后贡献搞成矩形之类的,扫描线一下。

这个题可以对点对是祖孙关系和两个子树关系的东西讨论,然后弄成矩形覆盖的贡献,直接扫描线就可以了。


Code:(点分)

#include <cstdio>
#include <cctype>
#include <vector>
#define ll long long
const int N=2e5+10;
template <class T>
void read(T &x)
{
	x=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
struct node
{
    int v,id;
    node(){}
    node(int V,int Id){v=V,id=Id;}
};
std::vector <node> yuu[N];
int n,m,Id[N];ll ans;
int head[N],to[N<<1],Next[N<<1],cnt;
void add(int u,int v)
{
	to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
int tag[N*30],sum[N*30],ch[N*30][2],tot,T,root[N];
#define ls ch[now][0]
#define rs ch[now][1]
#define ols ch[las][0]
#define ors ch[las][1]
int New()
{
	tag[++tot]=0,sum[tot]=0,ch[tot][0]=ch[tot][1]=0;
	return tot;
}
void pushdown(int now,int L,int R)
{
    if(L==R) return;
	if(tag[now])
	{
		int Mid=L+R>>1;
		if(!ls) ls=New();
        tag[ls]=tag[now];
        sum[ls]=Mid+1-L;
        if(!rs) rs=New();
        tag[rs]=tag[now];
        sum[rs]=R-Mid;
	}
}
void change(int las,int &now,int L,int R,int l,int r)
{
    if(l>r) return;
	pushdown(las,L,R);
	now=New();
	if(l==L&&r==R)
	{
		tag[now]=1;
		sum[now]=R+1-L;
		return;
	}
	int Mid=L+R>>1;
	if(r<=Mid) change(ols,ls,L,Mid,l,r),rs=ors;
	else if(l>Mid) ls=ols,change(ors,rs,Mid+1,R,l,r);
	else change(ols,ls,L,Mid,l,Mid),change(ors,rs,Mid+1,R,Mid+1,r);
	sum[now]=sum[ls]+sum[rs];
}
int query(int now,int L,int R,int l,int r)
{
	if(!now) return 0;
	if(l==L&&r==R) return sum[now];
	pushdown(now,L,R);
	int Mid=L+R>>1;
	if(r<=Mid) return query(ls,L,Mid,l,r);
	else if(l>Mid) return query(rs,Mid,R,l,r);
	else return query(ls,L,Mid,l,Mid)+query(rs,Mid+1,R,Mid+1,r);
}
int rt,si,mi,siz[N],del[N],buct[N],is[N],tim,key[N],tl;
int dfn[N],dfsclock;
void dfsrt(int now,int fa)
{
	int mx=0;siz[now]=1;
	for(int v,i=head[now];i;i=Next[i])
		if((v=to[i])!=fa&&!del[v])
		{
			dfsrt(v,now);
			siz[now]+=siz[v];
			mx=mx>siz[v]?mx:siz[v];
		}
	mx=mx>si-siz[now]?mx:si-siz[now];
	if(mx<mi) mi=mx,rt=now;
}
void dfs0(int now,int fa)
{
	siz[now]=1;
	dfn[now]=++dfsclock;
	is[now]=tim;
	for(int v,i=head[now];i;i=Next[i])
		if((v=to[i])!=fa&&!del[v])
		{
			dfs0(v,now);
			siz[now]+=siz[v];
		}
}
void dfs(int now,int fa)
{
    ++T;
    root[T]=root[T-1];
    for(int i=0;i<yuu[now].size();i++)
    {
        int v=yuu[now][i].v,id=yuu[now][i].id;
        ++buct[id];
        if(buct[id]==2)
        {
            key[++key[0]]=now;
            change(root[T],root[T],1,dfsclock,1,tl-1);
        }
        if(is[v]==tim&&dfn[v]<tl&&dfn[v]!=1)
            change(root[T],root[T],1,dfsclock,dfn[v],dfn[v]+siz[v]-1);
    }
    ans+=1ll*sum[root[T]];
	for(int v,i=head[now];i;i=Next[i])
		if((v=to[i])!=fa&&!del[v])
            dfs(v,now);
    for(int i=0;i<yuu[now].size();i++) --buct[yuu[now][i].id];
    --T;
}
void Divide(int now)
{
	del[now]=1;
	key[0]=dfsclock=0;
	++tim;
	dfs0(now,0);
	root[T=0]=tot=0;
	for(int i=0;i<yuu[now].size();i++) ++buct[yuu[now][i].id];
	for(int v,i=head[now];i;i=Next[i])
		if(!del[v=to[i]])
		{
		    T=0;//撤回到0但是东西前面树的东西还在
		    for(int j=1;j<=key[0];j++)
                change(root[T],root[T],1,dfsclock,dfn[key[j]],dfn[key[j]]+siz[key[j]]-1);
            key[0]=0;
            tl=dfn[v];
		    dfs(v,now);
		}
    for(int i=0;i<yuu[now].size();i++) --buct[yuu[now][i].id];
	for(int v,i=head[now];i;i=Next[i])
		if(!del[v=to[i]])
		{
			mi=si=siz[v];
			dfsrt(v,now);
			Divide(rt);
		}
}
int main()
{
	read(n),read(m);
	for(int u,v,i=1;i<n;i++)
		read(u),read(v),add(u,v),add(v,u);
	for(int a,b,i=1;i<=m;i++)
    {
        read(a),read(b);
        yuu[a].push_back(node(b,i));
        yuu[b].push_back(node(a,i));
	}
	mi=si=n,dfsrt(1,0),Divide(rt);
	ans=1ll*n*(n-1)/2-ans;
	printf("%lld\n",ans);
	return 0;
}

扫描线有机会补


2019.2.22

posted @ 2019-02-22 22:20  露迭月  阅读(247)  评论(0编辑  收藏  举报