数据结构 - cool's Blog

[莫队算法]

最近又看了一下莫队,发现我以前完全理解错了莫队算法。

继续阅读

[分块]bzoj4537

4537: [Hnoi2016]最小公倍数

Time Limit: 40 Sec  Memory Limit: 512 MB
Submit: 592  Solved: 251
[Submit][Status][Discuss]

Description

  给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值。所有权值都可以分解成2^a*3^b
的形式。现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得
路径依次经过的边上的权值的最小公倍数为2^a*3^b。注意:路径可以不是简单路径。下面是一些可能有用的定义
:最小公倍数:K个数a1,a2,…,ak的最小公倍数是能被每个ai整除的最小正整数。路径:路径P:P1,P2,…,Pk是顶
点序列,满足对于任意1<=i<k,节点Pi和Pi+1之间都有边相连。简单路径:如果路径P:P1,P2,…,Pk中,对于任意1
<=s≠t<=k都有Ps≠Pt,那么称路径为简单路径。

Input

  输入文件的第一行包含两个整数N和M,分别代表图的顶点数和边数。接下来M行,每行包含四个整数u、v、a、
b代表一条顶点u和v之间、权值为2^a*3^b的边。接下来一行包含一个整数q,代表询问数。接下来q行,每行包含四
个整数u、v、a和b,代表一次询问。询问内容请参见问题描述。1<=n,q<=50000、1<=m<=100000、0<=a,b<=10^9

Output

  对于每次询问,如果存在满足条件的路径,则输出一行Yes,否则输出一行 No(注意:第一个字母大写,其余
字母小写) 。

Sample Input

4 5
1 2 1 3
1 3 1 2
1 4 2 1
2 4 3 2
3 4 2 2
5
1 4 3 3
4 2 2 3
1 3 2 2
2 3 2 2
1 3 4 4

Sample Output

Yes
Yes
Yes
No
No

HINT

继续阅读

[分块]bzoj2002

一直听主力讲分块大法好,xllend3似乎证明莫队都可以用分块做!(%一发)

BZOJ传送门

一开始看到标签写着Splay 启发式合并感觉很厉害的样子,然后发现分块可以操!

思路:把N个蹦床分成sqrt(n)块,然后在每块里记录这个点飞出这个块要的步数和会飞到哪个点。
修改:把这个块里的点都重新算一遍,O(n^0.5)
询问:显然O(n^0.5)

#include <cstdio>
#include <cmath>
using namespace std;
#define maxn 200010
#define INF 0x7f7f7f7f
int a[maxn],p[maxn],num[maxn],size;
int LG(int x)//询问X属于哪个块
{
	return (x-1)/size+1;
}
int head(int x)//询问第x块的头
{
	return (x-1)*size+1;
}

int main()
{
	int n;
	scanf("%d",&n);
	for (int i = 1;i <= n;i++)
		scanf("%d",&a[i]);
	size = sqrt(n);
	if (size*size < n) size++;
	for (int i = n;i >= 1;i--)
	{
		if (i+a[i] > n)
			p[i] = INF,num[i] = 1;
		else if (LG(i+a[i])!=LG(i))
			p[i] = i+a[i],num[i] = 1;
		else p[i] = p[i+a[i]],num[i] = 1+num[i+a[i]];
	}
	int q;
	scanf("%d",&q);
	for (;q--;)
	{
		int x;
		scanf("%d",&x);
		if (x == 1)
		{
			int y,ans = 0;
			scanf("%d",&y);
			for (y++;y < INF;y = p[y])ans += num[y];
			printf("%d\n",ans);
		}
		else
		{
			int y,z;
			scanf("%d%d",&y,&z);
			a[++y] = z;
			int t = head(LG(y));
			for (;y >= t;y--)
			{
				if (y+a[y] > n)
					p[y] = INF,num[y] = 1;
				else if (LG(y+a[y])!=LG(y))
					p[y] = y+a[y],num[y] = 1;
				else p[y] = p[y+a[y]],num[y] = 1+num[y+a[y]];
			}
		}
	}
	return 0;
}

继续阅读

【并查集】bzoj1015

BZOJ传送门

每次删边显然十分复杂,所谓正难则反(= = 数学思路?)

只要把摧毁的建筑倒着加入并查集就行了

每次合并,连通块就会减1,但总的连通块要减去那些还未加入的星球(= = 貌似说不清楚)

直接上代码。

#include <cstdio>
#define maxm 200001
#define maxn 400001
int b[maxn],f[maxn],d[maxn],c[maxn],h[maxn],last[maxn],head[maxn],l;

void add(int x,int y)
{
	l++;
	h[l] = y;
	last[l] = head[x];
	head[x] = l;
}

int find(int x)
{
	if (f[x] == x) return x;
	return f[x] = find(f[x]);
}

void read(int &i)
{
	char x;
	for (x = getchar();x < '0';x = getchar());
	for (i = 0;x >= '0';x = getchar()) i = i * 10 + x - '0';
}

int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for (int i = 1;i <= m;i++)
	{
		int x,y;
		read(x);read(y);
		add(++x,++y);
		add(y,x);
	}
	int q;
	read(q);
	for (int i = 1;i <= n;i++)
		b[i] = 1,f[i] = i;
	for (int i = 1;i <= q;i++)
	{
		read(d[i]);
		b[++d[i]] = 0;
	}
	int t = q;\\t就是还没加入的星球个数
	int sum = n;
	for (int i = 1;i <= n;i++)
		if (b[i] == 1)
		{
			for (int x = head[i];x;x = last[x])
				if (b[h[x]] == 1)
				{
					int r1 = find(i),r2 = find(h[x]);
					if (r1 != r2)
					{
						f[r1] = r2;
						sum--;
					}
				}
		}
	
	for (int i = q;i >= 1;i--)
	{
		c[i] = sum - t;
		b[d[i]] = 1;
		for (int x = head[d[i]];x;x = last[x])
			if (b[h[x]] == 1)
			{
				int r1 = find(d[i]),r2 = find(h[x]);
					if (r1 != r2)
					{
						f[r1] = r2;
						sum--;
					}
			}
		t--;
	}
	printf("%d\n",sum-t);
	for (int i = 1;i <= q;i++)
		printf("%d\n",c[i]);
	return 0;
}

 

【倍增LCA】 NOIP2013 T3货车运输

人生第一道A掉的提高组T3= =

倍增+LCA 就能轻松AC了

但lbn大神让我去学树剖LCA 而我连树剖都不会 结果被大D一通

继续阅读




Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee