关于点分治的一些不知道算什么的东西

前言

scy出题出到了点分治,嗯,noip会考???于是我们都立刻去学了orzorzorz没有一道题不用调!还有一道题还没A。。蓝瘦

【偶然听到的对话】

scy:ljm,你觉得noip考点分治过分吗 (我:(蜜汁好奇o.o

男神:.....(点了点头)过分  (我:!!!真感人


关于点分治

点分治,,在我的理解就是在树上做分治= =...一般是处理一些其他什么treedp啊树剖啊做不了的路径问题。

每次找重心为根(让分治的层数最小啊),处理子树,统计经过重心的某些问题的答案,然后(=把重心删掉)分治下去各个子树,重复上面的操作。

嗯重心怎么找呢?根据定义(???),最大子树最小的就是重心,所以我们可以多用一个数组存以某点为根的最大子树的大小,根据这个判断比较找重心。还有一种方法(我就是用的这个),重心的各个子树大小都不超过节点总数的一半,也根据这个判断出重心。(一棵树的重心最多只有两个

注意事项:

1、重心不要找错,否则会让递归层数变多,从而TLE

2、处理当前子树的时候,先不要把当前子树的信息更新进去,等统计完了当前子树与之前子树的答案再更新。否则可能找到的答案的两点位于同一子树,而他们之间距离啊什么一些要统计的东西也许并不等于分别到重心的xxxx的和,这样就错了。

3、不要用memset去清空数组!会很慢!可以用多一个数组存修改了什么,最后根据数组来清掉。


也就做了四道题

嗯这四道题都是czy、hzwer等神犇的胡策题

T1 原题poj2114

[题意]boatherds 2s 64M by czy

求一颗树上距离为K的点对是否存在

[题解]

因为K不算大..一千万,就直接开个数组w[i]存是否存在某个点到重心的距离为i.于是到子树统计答案的时候,直接判断w[K-d[x]]是否为true就好了(额d[x]表示x这个点到重心的距离。

然后多组数据啊多组询问啊'.'是一组数据结束了就加的不是所有数据结束了才加。为此贡献了几次WA。

多组询问的话,在线做每问一下就重新找重心什么的 会比 离线做只做一次点分治统计答案的时候for一下询问 要慢一点。。嗯差距感觉不算大。不过在线做的话,已经判断到存在就能return出来(但是这样可能导致一些该清掉的数组没有清掉如果return的位置摆放不对)。

#pragma comment(linker,"/STACK:102400000,102400000")
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MX 10000010
#define maxn 11000

struct node
{
	int y,c,next;
}a[maxn*4];int len,first[maxn];
bool mark[maxn],w[MX],ans[110];//mark标记某点是否成为过重心(成为过重心的要删掉啊分治什么的
int siz[maxn],d[maxn];//siz存以某点为根的子树大小 d、w意义如文字所述
int s[maxn*10],sl,K[110];
int sta[maxn*10],tp,m;
void ins(int x,int y,int c)
{
	a[++len].y=y;a[len].c=c;
	a[len].next=first[x];first[x]=len;
}
void dfs_root(int x,int fa,int sum,int &root)//找重心
{
	siz[x]=1;bool ok=1;
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (y==fa || mark[y]) continue;
		dfs_root(y,x,sum,root);
		if (siz[y]*2>sum) ok=0;
		siz[x]+=siz[y];
	}
	if (2*(sum-siz[x])>sum) ok=0;
	if (ok) root=x;
}
void DFS(int x,int fa)//处理子树
{
	for (int i=1;i<=m;i++)//扫一遍所有询问
	 if (K[i]>=d[x] && w[K[i]-d[x]]) ans[i]=1;
	// if (K>=d[x] && w[K-d[x]]) {ans=1;return;}
	siz[x]=1;sta[++tp]=d[x];
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (y==fa || mark[y]) continue;
		d[y]=d[x]+a[k].c;
		DFS(y,x);siz[x]+=siz[y];
	}
}
void dfs(int x,int sum)
{
	dfs_root(x,0,sum,x);
	sl=0;s[++sl]=0;
	w[0]=1;d[x]=0;
	mark[x]=1;//mk[++mp]=x;
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (mark[y]) continue;
		d[y]=a[k].c;
		tp=0;DFS(y,x);
		// if (ans) break;
		while (tp)
		{
			w[sta[tp]]=1;s[++sl]=sta[tp--];
		}
	}
	while (sl) w[s[sl--]]=0;
	// if (ans) return;
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (mark[y]) continue;
		dfs(y,siz[y]);
		//dfs(y,(siz[y]*2>sum)?sum-siz[y]:siz[y]);
		// if (ans) return;
	}
}
int main()
{
	int i,n,x,y,c;
	while (1)
	{
		scanf("%d",&n);
		if (n==0) break;
		len=0;memset(first,-1,sizeof(first));
		for (i=1;i<=n;i++)
		{
			while(1)
			{
				scanf("%d",&x);
				if (x==0) break;
				scanf("%d",&c);
				ins(i,x,c);ins(x,i,c);
			}
		}
		// for (i=1;i<n;i++)
		// {
			// scanf("%d%d%d",&x,&y,&c);
			// ins(x,y,c);ins(y,x,c);
		// }
		m=0;
		while (1)
		{
			scanf("%d",&x);
			if (x==0) break;
			K[++m]=x;ans[m]=0;
		}
		// for (i=1;i<=m;i++) {scanf("%d",&K[i]);ans[i]=0;}
		dfs(1,n);
		for (i=1;i<=n;i++) mark[i]=0;
		for (i=1;i<=m;i++)
		 if (!ans[i]) printf("NAY\n");
		 else printf("AYE\n");
		printf(".\n");
	}
	return 0;
}


T2 原题 bzoj3648

[题意]tree 1s 128M by hzw

czy神犇种了一棵树,他想知道地球的质量

给定一棵n个点的树,求树上经过点的个数≥K的路径数量ans

对于部分数据,树上某两点间会多出最多一条无向边

[题解]

环套树...极度恶心..我调了一下午终于在激动中过了他们胡策的数据,然后交上去bzoj——WA!QwQmama我再也不做了

这个题主要是处理环..所以我们要先找环..然后可以发现,去掉环上的一边就变成了树啊,就是普通的点分治了。但是这样会少统计经过这条边的答案。那就单独处理。设这条边的两个点是x-y,枚举从环上哪些子树的点走到x,然后把它们到x的距离预处理进去,环上其他子树走到y,统计答案。

贴个在bzojWA的代码↓求指错啊!!!拍小数据拍不出来啊><(啥?大数据?能调吗???

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
#define maxn 100010

const int mx=100000;
struct node
{
	int x,y,next;
}a[maxn*2];int len,first[maxn];
void ins(int x,int y)
{
	a[++len].x=x;a[len].y=y;
	a[len].next=first[x];first[x]=len;
}
bool mark[maxn];LL ans;
int siz[maxn],d[maxn];LL c[maxn];
int s[maxn],sl,sta[maxn],tp,K;
bool bo[maxn];
stack<int> q;
int tot,n,del,cir[maxn],cnt;//cir[]存环上有哪些点,cnt存环上有几个点
int lowbit(int x) {return x&(-x);}
void change(int x,LL k)
{
	tot+=k;
	for (x;x<=mx;x+=lowbit(x)) c[x]+=k;
	// for (x;x>0;x-=lowbit(x)) c[x]+=k;
}
int query(int x)
{
	LL ret=0;
	// for (x;x<=mx;x+=lowbit(x)) ret+=c[x];
	// if (x==0) return ret;
	for (x;x>0;x-=lowbit(x)) ret+=c[x];
	return ret;
}
void dfs_root(int x,int fa,int sum,int &root)
{
	siz[x]=1;bool bk=1;
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (y==fa || mark[y] || (x==a[del].x && y==a[del].y) || (x==a[del].y && y==a[del].x)) continue;
		dfs_root(y,x,sum,root);
		siz[x]+=siz[y];
		if (siz[y]*2>sum) bk=0;
	}
	if (2*(sum-siz[x])<=sum && bk) root=x;
}
void DFS(int x,int fa)
{
	siz[x]=1;d[x]=d[fa]+1;
	// if (K>=d[x]) ans+=query(K-d[x]+1);
	// else ans+=query(1);
	if (K>=d[x]) ans+=tot-query(K-d[x]);
	else ans+=tot;
	sta[++tp]=d[x];
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (y==fa || mark[y] || (x==a[del].x && y==a[del].y) || (x==a[del].y && y==a[del].x)) continue;
		DFS(y,x);
		siz[x]+=siz[y];
	}
}
void dfs(int x,int sum)
{
	dfs_root(x,0,sum,x);sl=0;
	mark[x]=1;d[x]=0;s[++sl]=0;change(2,1);
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (mark[y] || (x==a[del].x && y==a[del].y) || (x==a[del].y && y==a[del].x)) continue;
		DFS(y,x);
		while (tp) {change(sta[tp]+2,1);s[++sl]=sta[tp--];}
	}
	while (sl) change(s[sl--]+2,-1);
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (mark[y] || (x==a[del].x && y==a[del].y) || (x==a[del].y && y==a[del].x)) continue;
		dfs(y,siz[y]);
	}
}
bool find_circle(int x,int fa)//我麻烦地找环
{
	if (bo[x])
	{
		for (int i=1;i<=n;i++) bo[i]=false;
		while (1)
		{
			int y=q.top();
			cir[++cnt]=y;
			bo[y]=true;
			q.pop();
			if (y==x) break;
		}return true;
	}bo[x]=true;q.push(x);
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (y==fa) continue;
		if (find_circle(y,x)) 
		{
			if (y==cir[cnt]) del=k;//del存的是去掉哪条边
			return true;
		}
	}bo[x]=false;q.pop();
	return false;
}
vector<int> v[maxn];
void ex_dfs(int x,int fa,int num,int nt)
{
	d[x]=d[fa]+1;
	change(d[x]+num+1,1);
	v[nt].push_back(d[x]+num+1);
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (y==fa || bo[y]) continue;
		ex_dfs(y,x,num,nt);
	}
}
void ws_dfs(int x,int fa,int num)
{
	d[x]=d[fa]+1;
	// if (K>=d[x]+num) ans+=query(K-d[x]-num+1);
	// else ans+=query(1);
	if (K>=d[x]+num) ans+=tot-query(K-d[x]-num);
	else ans+=query(mx);
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (y==fa || bo[y]) continue;
		ws_dfs(y,x,num);
	}
}
void ht_dfs()
{
	int i,j;dfs(1,n);d[0]=0;//K++;
	for (i=2;i<=cnt;i++)
	 ex_dfs(cir[i],0,cnt-i,i);//预处理其他子树到cir[1]的距离
	for (i=1;i<cnt;i++)
	{
		if (i!=1)
		{
			for (j=0;j<v[i].size();j++)
			 change(v[i][j],-1);
		}
		ws_dfs(cir[i],0,i-1);//cir[i]这个子树到cir[cnt]的距离
	}
}
int main()
{
	int m,x,y,i;
	scanf("%d%d%d",&n,&m,&K);//K--;
	len=0;memset(first,-1,sizeof(first));
	for (i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x);
	}ans=tot=0;del=-1;
	for (i=1;i<=n;i++) mark[i]=0;
	if (n!=m) dfs(1,n);
	else 
	{
		for (i=1;i<=n;i++) bo[i]=false;
		cnt=0;
		if (find_circle(1,0)) ht_dfs();
	}
	printf("%lld\n",ans);
	return 0;
}


T3 原题 bzoj2152

[题意]不虚就是要AK(czyak.c/.cpp/.pas)  2s 128M by zhb

czy很火。因为又有人说他虚了。为了证明他不虚,他决定要在这次比赛AK。

现在他正在和别人玩一个游戏:在一棵树上随机取两个点,如果这两个点的距离是4的倍数,那么算czy赢,否则对方赢。现在czy想知道他能获胜的概率。

*最终输出的概率要求分数的分子和分母的和尽量小且非负数

本题多组数据。对于每组数据:

第一行一个数n,表示树上的节点个数

接下来n-1条边a,b,c描述a到b有一条长度为c的路径

[题解]

这个就存mod 4的值就好了,其他同第一题没区别,感觉还简单一点(用的数组小多了!。

bzoj上是3的倍数,那就改成mod 3...就好了

话说,选的那两个点可以是同一个点啊,也算是一种情况。输不出样例以为代码错了..画了一下图才发现..

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 100010

struct node
{
	int x,y,c,next;
}a[maxn*2];int len,first[maxn];
int v[5],siz[maxn];
int sta[maxn],tp;
int s[maxn],sl;
int d[maxn],ans;
bool mark[maxn];
void ins(int x,int y,int c)
{
	a[++len].x=x;a[len].y=y;a[len].c=c;
	a[len].next=first[x];first[x]=len;
}
void dfs_root(int x,int fa,int sum,int &root)
{
	siz[x]=1;bool bk=1;
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (y==fa || mark[y]) continue;
		dfs_root(y,x,sum,root);
		siz[x]+=siz[y];
		if (siz[y]*2>sum) bk=0;
	}
	if (2*(sum-siz[x])>sum) bk=0;
	if (bk) root=x;
}
void DFS(int x,int fa)
{
	ans+=v[(4-d[x]+4)%4];
	siz[x]=1;sta[++tp]=x;
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (y==fa || mark[y]) continue;
		d[y]=(d[x]+a[k].c)%4;
		DFS(y,x);siz[x]+=siz[y];
	}
}
void dfs(int x,int sum)
{
	dfs_root(x,0,sum,x);
	mark[x]=1;v[0]++;d[x]=0;sl=0;s[++sl]=0;
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (mark[y]) continue;
		d[y]=(d[x]+a[k].c)%4;
		DFS(y,x);
		while (tp)
		{
			int p=sta[tp--];
			v[d[p]]++;s[++sl]=d[p];
		}
	}
	while (sl) v[s[sl--]]--;
	for (int k=first[x];k!=-1;k=a[k].next)
	{
		int y=a[k].y;
		if (mark[y]) continue;
		dfs(y,siz[y]);
	}
}
int gcd(int x,int y)
{
	if (y==0) return x;
	return gcd(y,x%y);
}
int main()
{
	//freopen("czyak.in","r",stdin);
	//freopen("czyak.out","w",stdout);
	int n,x,y,c,i,fm,fz;
	while (1)
	{
		scanf("%d",&n);
		if (n==0) break;
		len=0;memset(first,-1,sizeof(first));
		for (i=1;i<n;i++)
		{
			scanf("%d%d%d",&x,&y,&c);
			ins(x,y,c);ins(y,x,c);
		}ans=0;
		// memset(v,0,sizeof(v));
		for (i=1;i<=n;i++) mark[i]=0;
		dfs(1,n);
		fm=n*n;
		fz=ans*2+n;
		int gd=gcd(fz,fm);
		fz/=gd;fm/=gd;
		printf("%d/%d\n",fz,fm);
	}
	return 0;
}


T4 原题 hdu4812

这个在之前的“第四场膜你赛总结"有..就是膜你赛考了这题啊,还不会点分治啊qwq


结尾

一!定!要!熟!练!不然遇到了还是先打完其他题再做了。。毕竟打了也不一定能对= =

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值