[笔记]SD2021省队第一轮集训Day2

Day2 数据结构

DFS序线段树|字符串hash|多源BFS

有关998244353

它的二进制表达是:111011100000000000000000000001
它是一个质数。
它可以表达为两个数的平方和:998244353 = 3943^2 + 31348^2
它是勾股数之一:998244353^2 = 247210328^2 + 967149855^2
它可以被表达为:998244353 = 7 * 17 * 2^23 + 1

但是这些和左部L=[0,998244352],右部R=[0,998244352]R=(L*2333333+[1,26])%998244353又有什么关系呢?

T1 DFS序线段树

权值的设定

权值设置为与编号模n同余。

考场的想法

树链剖分|最小生成树|由于是集合制约所以不能2-SAT|树分治。

std真的写了一个kruskal。

正解是DP。

T2 字符串hash

•这题比较怪。

•考虑我们有个 O(n^2 ) 做法,就是求每个子串哈希值取最大。

•考虑字符串随机的条件,再结合哈希函数的随机性,基本上意味着哈希值是在 [0,m) 随机分布的。

•随机的 Θ(n^2 ) 个变量的最大值期望是 m-m/n^2 附近。

•考虑从大往小 check 这个值是否在哈希值中存在。

截止到这里我考场上想到了。但是检查一个哈希值是否能在字符串中出现就不会了。

•一个子串 (l,r] 的哈希值大概是 s_r-base^(r-l) s_l。

•那么 s_r-base^(r-l) s_l=x 即 b^(-r) s_r=x+b^(-l) s_l,扫一遍,维护个哈希表查是否存在即可。

•这样时间复杂度是 O(m/n^2 ⋅n)=O(m/n)。

•所以 min⁡(O(n^2 ),O(m/n))=O(max⁡(m^(2∕3),n) )。比如对于 n≤2000 跑暴力,否则跑这个做法即可。

T2 多源点BFS

•测试点 1∼3:模拟。

•测试点 4∼6:对每个 1≤t≤k 求 dis⁡(x,y)=t 的数量,即是要求矩形并,扫描线可做。

考场上想了扫描线,但是无奈我不会写。纠结于是处理这\(n\)个点还是先连出来边再处理边。

•测试点 7∼10:看起来就可以推式子。

•测试点 11∼16:在数轴上的情况,只有相邻两个区域合并的时候会有影响,处理出这些时间点,BFS 到的面积应该是一个以这些时间点间隔的分段函数,每段内是一个二次函数,要求每段的总和之和,每段内的总和是一个三次函数,可以插值出来。

•测试点 17∼20:不知道干啥用的,就放那。

扫描线:

#include <algorithm>
#include <cstdio>
#include <cstring>
#define maxn 300
using namespace std;

int lazy[maxn << 3];  // 标记了这条线段出现的次数
double s[maxn << 3];

struct node1 {
  double l, r;
  double sum;
} cl[maxn << 3];  // 线段树

struct node2 {
  double x, y1, y2;
  int flag;
} p[maxn << 3];  // 坐标

//定义sort比较
bool cmp(node2 a, node2 b) { return a.x < b.x; }

//上传
void pushup(int rt) {
  if (lazy[rt] > 0)
    cl[rt].sum = cl[rt].r - cl[rt].l;
  else
    cl[rt].sum = cl[rt * 2].sum + cl[rt * 2 + 1].sum;
}

//建树
void build(int rt, int l, int r) {
  if (r - l > 1) {
    cl[rt].l = s[l];
    cl[rt].r = s[r];
    build(rt * 2, l, (l + r) / 2);
    build(rt * 2 + 1, (l + r) / 2, r);
    pushup(rt);
  } else {
    cl[rt].l = s[l];
    cl[rt].r = s[r];
    cl[rt].sum = 0;
  }
  return;
}

//更新
void update(int rt, double y1, double y2, int flag) {
  if (cl[rt].l == y1 && cl[rt].r == y2) {
    lazy[rt] += flag;
    pushup(rt);
    return;
  } else {
    if (cl[rt * 2].r > y1) update(rt * 2, y1, min(cl[rt * 2].r, y2), flag);
    if (cl[rt * 2 + 1].l < y2)
      update(rt * 2 + 1, max(cl[rt * 2 + 1].l, y1), y2, flag);
    pushup(rt);
  }
}

int main() {
  int temp = 1, n;
  double x1, y1, x2, y2, ans;
  while (scanf("%d", &n) && n) {
    ans = 0;
    for (int i = 0; i < n; i++) {
      scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
      p[i].x = x1;
      p[i].y1 = y1;
      p[i].y2 = y2;
      p[i].flag = 1;
      p[i + n].x = x2;
      p[i + n].y1 = y1;
      p[i + n].y2 = y2;
      p[i + n].flag = -1;
      s[i + 1] = y1;
      s[i + n + 1] = y2;
    }
    sort(s + 1, s + (2 * n + 1));  // 离散化
    sort(p, p + 2 * n, cmp);  // 把矩形的边的纵坐标从小到大排序
    build(1, 1, 2 * n);       // 建树
    memset(lazy, 0, sizeof(lazy));
    update(1, p[0].y1, p[0].y2, p[0].flag);
    for (int i = 1; i < 2 * n; i++) {
      ans += (p[i].x - p[i - 1].x) * cl[1].sum;
      update(1, p[i].y1, p[i].y2, p[i].flag);
    }
    printf("Test case #%d\nTotal explored area: %.2lf\n\n", temp++, ans);
  }
  return 0;
}

例1 Room

给定一个由小写字母组成的 n×m 的矩阵,定义一个房间是同字母的极大四连通块。

q 次询问,每次给出一个矩形,问有多少房间与矩形有交。

数据范围:n,m≤2000,q≤5000。

处理联通块。联通的充要条件是一个矩形既在另一个矩形里面又在外面。

例2 Xor-matic Number of the Graph

给定一张 n 个点 m 条边的无向图,边有非负整数边权。

请求出所有满足 u 和 v 之间存在一条异或和为 w 的路径且 u<v 的三元组 (u,v,w) 的 w 之和。

数据范围:n≤〖10〗5,m≤2×〖10〗5,边权 ≤〖10〗^18。

知识点:线性基

例3 Bajtocja

u给定 d 张无向图,每张图都有 n 个点,初始时没有边。

u接下来 m 次操作,每次操作给定 (k,a,b),在第 k 张图中连边 (a,b)。

u每次操作后输出点对 (u,v) 的数量,满足 u,v 在每张图中都是连通的。

u

u数据范围:d≤2000,n≤5000,m≤〖10〗^6。

哈希??

我们需要知道的只是每对点之间是否连通,即在同一张图所属的连通块是否一样 于是我们对每个点在d张图中所属的连通块标号进行哈希,这个哈希要能快速删除一个标号 插入一个标号 如果有两个点哈希后的值相同,那么这两个点在d张图中都连通。于是我们再对这个哈希值做一遍哈希,来计算相同哈希值的个数 连边时用启发式合并,每次将size小的连通块全部修改fa,总复杂度O(dn\log n)O(dnlogn)

//75500kb	4628ms
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
typedef unsigned long long ull;
const int N=5005,D=205,M=1e6+5,mod=2e6;
const ull seed=769;

int n,m,d,H[D][N],Enum,to[M<<1],nxt[M<<1],fa[D][N],sz[D][N],Ans;
ull hs_id[N],Pow[D];
struct Hash_Table
{
	int top,h_H[mod+5],sk[mod],h_nxt[mod],cnt[mod];
	ull val[mod];
	void Init()
	{
		top=mod-5;
		for(int i=1; i<=top; ++i) sk[i]=i;
	}
	void Insert(ull x)
	{
		int p=x%mod;
		for(int i=h_H[p]; i; i=h_nxt[i])
			if(val[i]==x) {Ans+=2*cnt[i]+1,++cnt[i]; return;}
		++Ans;//(a,a)也算一对 
		int pos=sk[top--];
		val[pos]=x, cnt[pos]=1, h_nxt[pos]=h_H[p], h_H[p]=pos;
	}
	void Delete(ull x)
	{
		int p=x%mod,pre=h_H[p];
		if(val[pre]==x)
		{
			Ans-=2*cnt[pre]-1;
			if(!--cnt[pre]) sk[++top]=pre, h_H[p]=h_nxt[pre];
		}
		else
			for(int i=h_nxt[pre]; i; pre=i,i=h_nxt[i])
				if(val[i]==x)
				{
					Ans-=2*cnt[i]-1;
					if(!--cnt[i]) sk[++top]=i, h_nxt[pre]=h_nxt[i];
					break;
				}
	}
}hs2;

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 AddEdge(int u,int v,int k){
	to[++Enum]=v, nxt[Enum]=H[k][u], H[k][u]=Enum;
}
void DFS(int x,int f,int k,int anc)
{
	hs2.Delete(hs_id[x]);
	hs_id[x]-=Pow[k]*fa[k][x];//fa就是belong了 
	fa[k][x]=anc;
	hs_id[x]+=Pow[k]*anc;
	hs2.Insert(hs_id[x]);
	for(int i=H[k][x]; i; i=nxt[i])
		if(to[i]!=f) DFS(to[i],x,k,anc);
}
void Union(int u,int v,int k)
{
	if(fa[k][u]==fa[k][v]) return;
	if(sz[k][fa[k][u]]<sz[k][fa[k][v]]) std::swap(u,v);
	sz[k][fa[k][u]]+=sz[k][fa[k][v]];
	DFS(v,u,k,fa[k][u]);
	AddEdge(u,v,k),AddEdge(v,u,k);
}

int main()
{
	d=read(),n=read(),m=read();
	Pow[0]=1;
	for(int i=1; i<D; ++i) Pow[i]=Pow[i-1]*seed;
	hs2.Init();
	for(int i=1; i<=n; hs2.Insert(hs_id[i++]))
		for(int j=1; j<=d; ++j)
			fa[j][i]=i, sz[j][i]=1, hs_id[i]+=Pow[j]*i;//Hash = (∑s[i]seed^i) mod 2^{31}
	int a,b,k;
	while(m--)
		a=read(),b=read(),k=read(),Union(a,b,k),printf("%d\n",Ans);
	return 0;
}

例4 Rally

u给定一张 n 个点 m 条边的 DAG,每条边长度为 1。

u你可以删除一个点,最小化删除后的最长路径长度。

u

u数据范围:2≤n≤5×〖10〗5,m≤〖10〗6。

建立超级源点超级汇点。

考虑topo序。计算每个点到超级源点和超级汇点的距离。

例5 Tournament cycle

竞赛图,缩点后变成链。

竞赛图,一定有哈密顿链。

强连通竞赛图,缩点后变成一个点。

强连通竞赛图,一定有哈密顿回路。

强连通竞赛图,每个点都经过长度为3、4、5...n的简单环。?

若存在长度为k-1的环则一定存在长度为k的环。

-----------------------------以上为昨天图论内容-------------------------------

例1 Siano

u有 n 亩土地,一开始都是空的,一个农夫要在上面种草。

u其中,第 i 亩土地的草每天会长高 a_i 厘米。

u农夫一共会进行 q 次收割,其中第 i 次收割在第 d_i 天,并把所有高度大于等于 b_i 的部分全部割去。

u输出每次收割得到的草的高度总和。

u

u数据范围:n,q≤5×〖10〗5,1≤a_i≤〖10〗6,1≤d_i,b_i≤〖10〗^12。
且 d_i<d_(i+1),任意时刻不存在一亩草高度超过 〖10〗^12。

草的高度递增排列,每次区间割下一些草仍然是单调不降的

线段树二分即可

区间修改也适用

例2 二分图

例3 直径

u给定一棵树,q 次询问,给定 a,b,c,d,求 x∈[a,b] 且 y∈[c,d] 时 dis(x,y) 的最大值。

u

u数据范围:n,q≤〖10〗^5。

例4 Painting Edges

u给定一张 n 个点 m 条边的无向图,每条边有编号在 [0,k] 内的颜色。

u初始时每条边的颜色都是 0。

u执行 q 次操作,每次给定 (e,c) 表示将边 e 的颜色改成 c(c∈[1,k])。

u如果一次操作后只考虑某个非零颜色的图不是二分图了,则忽略这次操作。

u输出每次操作是否被执行(即这次操作没被忽略)。

u

u数据范围:n,m,q≤5×〖10〗^5,k≤50。

线段树分治,内维护50个并查集。

例5 Segment

u在平面直角坐标系中维护两个操作:

1.加入一条线段,编号为 i。

2.询问与竖线 x=k 相交的线段中,交点最高的线段的编号。

u强制在线。

u

u数据范围:1≤横坐标≤40000,q≤〖10〗5,1≤纵坐标≤〖10〗9。

例6 楼房重建

给定一个长度为
n 的序列,q 次操作,每次修改一个数,然后求有多少个数满足它是前缀最大值。

数据范围:n,q≤〖10〗^5。

例7

例8 线段树

u给定一个长为 n 的数组 a。

u有 m 个操作,编号为 1∼m,每个操作形如把区间 [l,r] 内的数置为这个区间的最大值。

u有 q 次操作:

1.给定 k,v,修改 a_k←v。

2.给定 x,y,k,询问如果依次执行编号为 x∼y 的操作,a_k 将会是多少。

u

u数据范围:n,m,q≤〖10〗^5。

合并操作,倒序维护。

倍增

用于合并操作。

例9 树上的路径(***) P2048 [NOI2010] 超级钢琴

u给定一棵 n 个结点的树,每条边有正整数权值。

u求出 dis(a,b)(1≤a<b≤n)从小到大排序后的前 m 个值。

u

u数据范围:n≤5×〖10〗4,m≤3×〖10〗5。

点分治!nlogn个点

考虑重心

维护前缀子树堆,堆内元素为<点,?>

考虑依次加入子树

长度logn的超级gangxing??

例10 等差数列

u给定一个长度为 n 的数列 a_1∼a_n。

u有 q 次操作,每次要么修改数列某一项,要么给出 l,r,k,询问区间 a_l∼a_r 中的数从小到大排序后能否形成公差为 k 的等差数列。

u强制在线。

u

u数据范围:n,q≤3×〖10〗5,0≤a_i,k≤〖10〗9。

性质1:所有差分的gcd为k

性质2:没有相等的数(公差不为0)

性质3:最大值与最小值之差为公差*(个数-1)

以上性质为充要条件。

扫描线:判重

主席树:在线判重

例11 Good Subsegments

u给定一个长度为 n 的排列。

u有 q 次询问,每次给定一个区间,求这个区间的所有是连续段的子区间个数。

u连续段的定义是排序后形成公差为 1 的等差数列。

u

u数据范围:n,q≤1.2×〖10〗^5。

线段树看起来可做的样子。维护一个区间的最大、小值。

正解

回滚莫队 固定右端点 算左端点合法连续段的数量

左端点线段树下标,右端点时间轴

q = 1

扫描线

线段树

区间加法 最小值

复杂度

均摊??

区间加法历史版本最小值 吉司机线段树??

例12 Puzzled Elena

u给定一棵 n 个结点的有根树,每个点有正整数权值 a_i。

u对于每个点,输出它的子树中有多少个点的权值与它的权值互质。

u

u数据范围:n≤〖10〗5,a_i≤〖10〗5。

莫比乌斯

子树=dfs序上的一个区间=前缀相减

gcd = 1,枚举因数

例13 Odwiedziny

u给定一棵 n 个结点的树,每个点有点权。

u有 q 次询问,每次给定 x,y,k,表示从 x 出发每次朝 y 方向跳 k 条边的距离,如果当前点和 y 距离不超过 k 就直接一步跳到 y,请求出经过的所有点的点权和。

u

u数据范围:n≤〖10〗^5。

根号n分治

posted @ 2021-05-19 18:33  咕咕坤  阅读(96)  评论(0编辑  收藏  举报