Logo KSCD_ 的博客

博客

标签
暂无

AT_kupc2020_m 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-03-03 11:50:20

前段时间模拟赛做到的题,是同学分享的做法,感觉妙,写个题解。

先不考虑 $k$ 的限制,通过容斥计算答案。设不考虑限制时答案为 $f_{n,m}$,则再钦定一部分盒子内放长为 $2k$ 的括号序列,容斥一下可以得到答案为 $\sum_{i=0}^{\min(n,\lfloor\frac mk\rfloor)} (-1)^i\times {n\choose i} \times C_k^i\times f_{n-i,m-ik}$,其中 $C_k^i$ 为卡特兰数第 $k$ 项的 $i$ 次方,表示所钦定盒子内的方案数。因此只需要实现多次求解 $f_{n,m}$ 即可。

考虑把 $n$ 个串塞到一个新串里,构造一个双射。方式是在新串开头预先放置 $(n-1)$ 个左括号,后面继续构造合法括号串,要求后面部分还要再加入 $m$ 个左括号。对新串做括号匹配,以与前 $(n-1)$ 个左括号匹配的 $(n-1)$ 个右括号为分界点,把后面分成 $n$ 部分,每一部分均为合法括号串且总长为 $2m$,感性理解可以证明构成双射。

所以 $f_{n,m}$ 等价于长为 $2(n+m-1)$ 且开头 $(n-1)$ 位均为左括号的合法括号串数量。考虑反射容斥求解,经典地把左括号看成 $1$,右括号看成 $-1$,并以下标和前缀和建立坐标系。则答案即为从 $(n-1,n-1)$ 走到 $(2n+2m-2,0)$,且全程不跨过 $x$ 轴的方案数。推一推式子发现无后一条限制时方案数为 ${n+2m-1}\choose m$,也即在后面部分的 $(n+2m-1)$ 个位上随便选 $m$ 个放左括号。

那么还要减去跨过 $x$ 轴的方案数。注意到这些方案都经过了 $y=-1$ 这条直线,考虑把这种方案第一次经过 $y=-1$ 之后的部分沿 $y=-1$ 对称,终点变为 $(2n+2m-2,-2)$,感性理解可以证明起点到新终点的路径与原来的不合法路径形成双射,所以推一推得到不合法方案数为 ${n+2m-1}\choose {m-1}$,因此也有 $f_{n,m}={{n+2m-1}\choose m}-{{n+2m-1}\choose {m-1}}$。

这样就做完了,预处理阶乘和逆元,时间复杂度线性,这是代码

P9339 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-03-03 16:16:12

以下设 $\sum a_i=s$,同时认为 $n,m,s$ 同级。

首先发现盒子和饼干之间是类似匹配的关系,因此我们以盒子为左部点,每种饼干为右部点,连边即为完全二分图,代表每种饼干只能装到每个盒子中一个。题目所求即为最少的左部点数量使得该图有完美匹配,输出方案。

完美匹配考虑霍尔定理,首先要 $\sum b=\sum a$,然后要求对于所有左部点集合 $S$,都满足 $\sum_{i\in S}b_i\le\sum_{j=1}^n\min(a_j,\left|S\right|)$,这里要对 $a_j$ 取最小值是因为这种饼干最多只能贡献 $a_j$ 的出度。

我们注意到等式右边的取值只与集合大小有关,可以对 $a$ 排序后预处理固定集合大小下的上界 $c$。此时只要 $b$ 最大的若干个的和不超过上界,则更小的一定也不超过,必然合法。问题转化为在 $b$ 中可重复地选尽可能少的数,要求和为 $s$,同时保证前 $i$ 大之和不超过 $c_i$。

直接使用背包,先对 $b$ 排序,设 $f_{i,j,k}$ 表示考虑了前 $i$ 大的 $b$,选了 $j$ 个,总和为 $k$ 是否可行,对所有 $k>c_j$ 的状态强制赋成 $0$ 即可保证限制,转移是完全背包,为 $f_{i,j,k}=f_{i-1,j,k} \ ee f_{i,j-1,k-b_i}$,这样就是 $O(n^3)$ 的。

但是我们注意到在 $f_{i,j,k}$ 中使用的 $b$ 至少是 $b_i$,因此 $j\le \lfloor\frac s {b_i}\rfloor$。又因为 $b_i$ 两两不同,所以所有 $j$ 的个数和不超过 $O(n\ln n)$,复杂度降为 $O(n^2\ln n)$。又注意到 DP 值是 $0$ 或 $1$,同时 $f_{i,j}$ 只会从 $f_{i-1,j}$ 和 $f_{i,j-1}$ 在 $k$ 这一维以相同的偏移量转移,因此可以把 $f_{i,j}$ 在 $k$ 这一维的 $s$ 个状态压到 bitset 里优化转移,最终复杂度为 $O(\frac{n^2\ln n}w)$。

至于输出方案,先在 DP 数组上倒着把 $b$ 的方案找出来,然后每次贪心地把 $a$ 最大的若干种各放一个在该盒子里并减 $1$,用堆维护这个过程即可,具体看下面代码。值得一提的是,如果没有方案要求,上面的 DP 其实是可以滚动数组优化的。

#include<iostream>
#include<algorithm>
#include<bitset>
#include<queue>
#define bs bitset <N>
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int N=15000+10;
int n,m,s,r=-1,a[N],b[N],p[N],c[N],w[N];
bool cmpa(int x,int y) {return a[x]<a[y];}
bool cmp(int x,int y) {return x>y;}
bs lm[N];
vector <bs> f[N];
vector <pii> tv;
priority_queue <pii> q;
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],s+=a[i],p[i]=i;
	cin>>m;
	for(int i=1;i<=m;i++) cin>>b[i];
	sort(p+1,p+1+n,cmpa),sort(b+1,b+1+m,cmp);
	int tp=0,ts=0;
	for(int i=1;i<=s;i++)
	{
		while(tp<n&&a[p[tp+1]]<=i) tp++,ts+=a[p[tp]];
		c[i]=ts+(n-tp)*i;
	}
	b[0]=s+1,lm[0][0]=1;
	for(int i=1;i<=s;i++) lm[i]=lm[i-1],lm[i][i]=1;
	for(int i=0;i<=m;i++) f[i].resize(s\/b[i]+1);
	f[0][0][0]=1;
	for(int i=1;i<=m;i++) for(int j=0;j<=s\/b[i];j++)
	{
		if(j<=s\/b[i-1]) f[i][j]=f[i-1][j];
		if(j) f[i][j]|=(f[i][j-1]<<b[i]);
		f[i][j]&=lm[c[j]];
	}
	for(int i=1;i<=s\/b[m];i++) if(f[m][i][s]) {r=i; break;}
	cout<<r<<'\n';
	if(r==-1) return 0;
	int cur=s,tw=m;
	for(int i=1;i<=r;i++) for(int j=tw;j>=1;j--) 
		if(f[j][r-i+1][cur]&&f[j][r-i][cur-b[j]])
			{w[i]=b[j],cur-=b[j],tw=j; break;}
	for(int i=1;i<=n;i++) q.push({a[i],i});
	for(int i=1;i<=r;i++)
	{
		cout<<w[i]<<' ',tv.clear();
		for(int j=0;j<w[i];j++)
		{
			pii te=q.top(); q.pop();
			te.fi--,cout<<te.se<<' ';
			if(te.fi) tv.push_back(te);
		}
		for(pii te:tv) q.push(te);
		cout<<'\n';
	}
	return 0;
}

P10107 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-03-03 21:34:55

考虑按位计算贡献,先考虑链的情况。考虑倍增,设 $f_{i,k}$ 表示 $[i,i+2^k)$ 区间的答案。转移时先累加两个小区间的贡献,另外后半部分的第 $(k-1)$ 位要异或 $1$,因此设 $g_{i,k}$ 表示前 $i$ 个数第 $k$ 位若异或 $1$ 会产生的总变化量。统计时若其该位为 $1$ 则贡献 $-1$,否则贡献 $1$。通过差分计算变化量,最终转移为 $f_{i,k}=f_{i,k-1}+f_{i+2^{k-1},k-1}+2^{k-1}\times(g_{i+2^k-1}-g_{i+2^{k-1}-1})$。统计答案时一直向后跳并累加即可。

考虑把这东西搬到树上,但由于子树内在某一层上的点不止一个,只能在 $i$ 节点本身上记录信息。所以设 $f_{i,j,k}$ 表示 $i$ 子树内深度在 $[d_i+j,d_i+j+2^k)$ 内所有点的贡献和。可以发现这个 $(i,j)$ 很能长链剖分,重儿子直接继承,轻儿子对应位置直接累加。但是这样没有计算 $f_{i,0}$,考虑怎么计算这个。

那么先把 $a_i$ 加到 $f_{i,0}$ 里。假如从子树 $v$ 给 $f_{i,0}$ 更新,设 $s_k$ 表示 $v$ 子树内对 $f_{i,0,k}$ 的贡献,有 $s_0=0$。发现 $s_k$ 可以用 $s_{k-1}$ 拼上 $f_{v,2^{k-1}-1,k-1}$ 得到,这里的式子应该和链的情况一样,实际实现时由于 $s_k$ 只从 $s_{k-1}$ 转移,开一个变量,一直更新就行。

那么问题就在于还需要一个记录贡献变化量的数组 $g$ 来辅助更新。链上我们是用前缀和维护的,但是树上长剖不好搞前缀和,所以考虑转而计算后缀和。具体地设 $g_{i,j,k}$ 表示 $i$ 子树内深度不低于 $d_i+j$ 的所有点权第 $k$ 位若异或 $1$ 会产生的总变化量,这就可以直接对应位累加了,只需要在 $g_{i,0}$ 上额外加入 $a_i$ 的贡献即可。

所有的转移和统计答案都跟链上类似,把每个询问扔到对应节点上处理即可。只是要格外注意在 $g$ 上差分时不能超出当前节点的管辖范围,有比较多的细节。时间复杂度 $O((n+q)\log n)$。

#include<iostream>
#include<vector>
#define pii pair<int,int>
#define ll long long
using namespace std;
const int N=1e6+10;
const int K=20+3;
int n,q,v[N],d[N],son[N]; ll res[N];
vector <int> e[N];
vector <pii> a[N];
struct nod{ll f[K],g[K];}temp[N],*f[N],*cur=temp;
void dfs(int u)
{
	d[u]=0;
	for(int v:e[u])
	{
		dfs(v),d[u]=max(d[u],d[v]);
		if(d[v]>d[son[u]]) son[u]=v;
	}
	d[u]++;
}
void merg(int u,int v)
{
	for(int j=0;j<20;j++) f[u][0].g[j]+=f[v][0].g[j];
	ll s=0;
	for(int p=0,k=1;k<20;p+=(1<<(k-1)),k++)
	{
		if(p<d[v]) s+=f[v][p].f[k-1]+(1<<(k-1))*f[v][p].g[k-1];
		if(p+(1<<(k-1))<d[v]) s-=(1<<(k-1))*f[v][p+(1<<(k-1))].g[k-1];
		f[u][0].f[k]+=s;
	}
}
void dfsb(int u)
{
	for(int j=0;j<20;j++) f[u][0].f[j]=v[u],f[u][0].g[j]=(((v[u]>>j)&1)?-1:1);
	if(son[u]) f[son[u]]=f[u]+1,dfsb(son[u]),merg(u,son[u]);
	for(int v:e[u]) if(v!=son[u])
	{
		f[v]=cur,cur+=d[v],dfsb(v),merg(u,v);
		for(int j=0;j<d[v];j++) for(int k=0;k<20;k++) 
			f[u][j+1].f[k]+=f[v][j].f[k],f[u][j+1].g[k]+=f[v][j].g[k];
	}
	for(pii te:a[u])
	{
		int x=min(te.first,d[u]-1),id=te.second;
		for(int p=0,k=19;k>=0;k--)
		{
			if(p+(1<<k)-1>x) continue;
			res[id]+=f[u][p].f[k],p+=(1<<k);
			if(p<d[u]) res[id]+=(1<<k)*f[u][p].g[k];
			if(x+1<d[u]) res[id]-=(1<<k)*f[u][x+1].g[k];
		}
	}
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n,d[0]=-1;
	for(int i=1;i<=n;i++) cin>>v[i];
	for(int i=2,x;i<=n;i++) cin>>x,e[x].push_back(i);
	cin>>q;
	for(int i=1;i<=q;i++)
	{
		int x,k; cin>>x>>k;
		a[x].push_back({k,i});
	}
	dfs(1),f[1]=cur,cur+=d[1],dfsb(1);
	for(int i=1;i<=q;i++) cout<<res[i]<<'\n';
	return 0;
}

25.02-MX 模拟赛题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-03-09 01:24:05

省选已经结束了,但是感觉梦熊模拟赛好题不少,所以文化课开课之前把题解补了下。

D1T1 删点 MST

题意

给定 $n$ 点 $m$ 边的无向连通图,对每个点求删掉该点及所连接的边后剩余图的最小生成树,不连通输出 $-1$。$n,m\le 5\times 10^5$。

题解

先跑出原图的最小生成树,那么删点后没被删掉的树边保留一定不劣,那么需要用非树边将 $d_x$ 个连通块连起来,其中 $d_x$ 表示所删掉点在树上的度数。

注意到一条非树边 $(u,v,w)$ 只会在 $(u,v)$ 树上路径上的点被删时才有可能被选,又注意到若被删的点不是 $LCA(u,v)$,则两点中必有一点是所删点的父亲连通块中的。所以路径上除 $LCA(u,v)$ 和与 $LCA(u,v)$ 相邻的点以外的点 $x$ 在父亲 $fa_x$被删掉时,都会存在一条权为 $w$,连接 $x$ 和 $fa_{fa_x}$ 连通块的边。注意到这种边除边权以外没有区别,所以拆成两次链取最小值即可,这样就维护出了每个点这样的最小边权。

另外对于 $LCA(u,v)$,其被删除时包含 $u$ 和包含 $v$ 的子树之间存在一条权为 $w$ 的边,因为 LCA 唯一,直接记录即可。对每个点求解时取出所有子节点向父亲的最小连边和记录的所有边,跑一遍 Kruskal 即可。这样点数总和是 $\sum d_i = O(n)$ 的,边数总和是 $O(n+m)$ 的,复杂度瓶颈为树剖的 $O(m\log^2 n)$。

D1T2 毒药

题意

给定一棵树,点有点权 $a_i$,求满足 $a_x\oplus a_y\le \bigoplus_{i\in Path(x,y)} a_i$ 的二元组 $(x,y)$ 个数。$n\le2\times 10^5,a_i\le 2^{60}$。

题解

记 $d_i$ 表示 $i$ 到根的路径异或和,则原条件转化为 $a_x\oplus a_y\le d_x\oplus d_y\oplus a_{LCA(x,y)}$。则容易想到枚举 LCA,统计不同子树之间点对的贡献。然而这样遍历的总次数没有保证,所以使用点分治,上式中 $d$ 也变成点到分治中心的路径异或和。则问题转化为动态维护集合 $S$,集合元素为 $(a,d)$ 二元组,多次询问 $(a_x,d_x)$,查询集合 $S$ 中有多少 $(a_y,d_y)$ 满足 $a_x\oplus a_y\le d_x\oplus d_y\oplus a_{rt}$。

发现只有令含 $x$ 项和含 $y$ 项分别在同侧,才能快速维护数量。但小于号没有交换律,所以先变不等号并移项,得到 $a_x\oplus d_x\oplus a_{rt}\ne a_y\oplus d_y$,然后按照 $a\oplus d$ 分类后枚举 LCP 长度,分讨不同的最高位,最后加上相等的贡献即可。实现只需开一棵以 $a\oplus d$ 为键值的 Trie 树,每个点记录子树内 $a$ 该位为 $0$ 或 $1$ 的个数,可通过 $a\oplus d$ 的情况得到 $d$ 该位的值。查询时从根到对应叶子遍历一条链,处理链上节点的兄弟节点即可。

原题

点分治后的部分:P7502

D2T1 跳跃树桩

题意

给出 $n$ 个树桩,每个树桩有位置 $x_i$ 和价值 $a_i$,从第一个树桩跳到第 $n$ 个,要求在 $x$ 时只能跳到 $[x+L,x+R]$ 内的树桩,求最终经过树桩的价值降序排序后最大的字典序。$n,a_i\le 5\times 10^5,x,L,R\le 10^9$。

题解

考虑如果直接 DP,有 $f_{i}=\max_{x_i-R\le x_j\le x_i-L} f_j + a_i$,这里对 $j$ 的限制显然可以单调队列优化,但是 max 是比较字典序,$a_i$ 也需要插入正确位置,直接暴力插入和比较是 $O(n^2)$ 的,无法通过。

发现如果在桶上记录每个数在序列中出现的次数,插入时变化的位置只有一个,比较时从大到小找到第一个数量不同的位置,较多的即为字典序较大的。使用可持久化线段树维护桶的哈希值,从而用线段树二分实现比较,单点修改即可。时空复杂度 $O(n\log n)$。

D2T2 清除敌人

题意

给定 $n$ 个点 $m$ 条边的有向无环图,起点为 $1$ 号点。可以沿着有向边移动,也可以在任意时刻直接返回 $1$ 号点。到达一个存在敌人的点时,需要消耗 $a_i$ 点血量清除,清除后恢复 $b_i$ 点血量,但整个过程中血量都不能低于 $0$。问最少需要多少初始血量才能清除所有敌人。$n+m\le72,a_i,b_i\le 10^{15}$。

题解

考虑如果没有任何顺序限制怎么做,此时若存在 $i,j$ 两个点,则先取 $i$ 需要的血量下限为 $\max(a_i,a_i-b_i+a_j)$,先取 $j$ 需要的血量下限为 $\max(a_j,a_j-b_j+a_i)$,注意到这样可以确定任意两个之间的顺序,且比较有传递性,所以按照这样的顺序清除即可。

那么把这个放到树上怎么做?考虑排序后第一个点,那么一旦其父亲被清除,其一定会被马上清除,所以可以将其与父亲合并为一个点,新值为 $a'=\max(a_f,a_f-b_f+a_x),b'=b_f-a_f+b_x-a_x+a'$,这样和原来的两个点先后清除等价。原先两个点的所有儿子都接在这个新点上,直到最后只剩一个节点时其 $a$ 值即为答案。

那 DAG 怎么做?注意到 $n+m$ 很小,所以 $n$ 比较小时直接 $O(2^nn)$ 状压 DP 解决,大概能做 $n\le24$。那么剩余情况下 $m\le48$,这时搜索出所有可能的外向树,对每种树做一遍刚才树上的做法,复杂度为 $O(cn\log n)$,其中 $c$ 为外向树方案数。考虑计算方案数的上界,此时 $n\ge 24,\sum d_i=m\le 48,c=\prod d_i$,其中 $d$ 为某个点的入度。根据均值不等式,所有 $d$ 均相等时 $c$ 最大,也即 $(\frac mn)^n$,在最大的 $n=24,m=48$ 时即为 $2^{24}$,总复杂度为 $O(2^{\frac {n+m}3}n\log n)$。

D2T3 括号序列

题意

给定 $n,m,k,mod$,求在 $n$ 个盒子中各放一个长度不为 $2k$ 的合法括号序列,总长为 $2m$ 的方案数,对 $mod$ 取模。$n,m,k\le 10^7$。

原题 + 题解

数据弱化版:AT_kupc2020_m题解

D3T1 简单函数

题意

给定 $n$ 个数 $x_i$,需要支持单点修改,询问 $i\in[1,n)$ 中是否存在 $i$ 满足 $f(x_i)f(x_{i+1})\le 0$,其中 $f(x)=(a\oplus x)-b$,每次询问给出 $a,b$。若存在则输出任意一个,否则输出 $-1$。$n,q\le 10^6,x,a,b<2^{30}$。

题解

考虑如果 $\max (a\oplus x)<b$ 或者 $\min(a\oplus x)>b$,则所有的 $f(x)$ 均同号,无解。否则 min 和 max 的 $f$ 值异号,考虑先找到这两个位置 $l,r(l<r)$,我们已知它们异号,则可以找到 $mid=\frac{l+r}2$,此时 $l,r$ 中一定存在与 $mid$ 异号的,所以判断一下即可把区间缩短到原来的一半,$O(\log n)$ 次即可将区间缩小到 $[i,i+1]$,输出即可。

实现上需要求出 $(a\oplus x)$ 的最值及其位置,所以对所有 $x$ 建 Trie 树,在叶子节点上记录对应的所有下标即可,可以使用 set 或可删堆,也可以在外面单开一个 set 记录值和下标的对应关系。时间复杂度 $O(q(\log V+\log n))$。

D3T2 异或大师

题意

给定长为 $n$ 的序列 $a$,可以任意次将 $a_i$ 赋成 $a_i\oplus a_{i-1}$,求操作后的最长上升子序列长度。$n\le 3\times 10^5,a_i\le 10^9$。

题解

注意到 $a_i$ 可以任意异或 $i$ 前面的所有数,也即可以在 $i$ 前面选出任意一个子集异或给 $i$,线性基就可以很好地表示出任选子集的异或值。

考虑直接设 $f_i$ 表示目前长为 $i$ 的上升子序列的最小结尾,暴力转移的话每次枚举所有的 $f_j$,找到线性基中所有数异或 $a_i$ 得到的大于 $f_j$ 的最小值,用这个值更新 $f_{j+1}$,复杂度 $O(n^2\log V)$。

又注意到线性基只会变化最多 $\log V$ 次,那么若在 $a_p$ 处改变,直接使用 $a_p$ 暴力转移,后面下一次改变之前的所有数取值方式均相同,考虑整体转移。假设相同的位数为 $d$,则先找到线性基中大于 $f_j$ 的最小值,求出其在线性基中的从小到大的排名 $k_j$。那么 $f_j$ 后面最多可以加上 $\min(siz-k_j+1,d)$ 个数,$siz$ 为线性基能表示的数的个数。

那么若由 $f_j$ 转移到 $g_i$,则 $g_i$ 的值为线性基中第 $(k_j+i-j-1)$ 小的数,由于 $f_j$ 能转移的 $i$ 有上界,用单调队列维护目前可以用来转移的 $j$,$k_j-j$ 较小的较优,转移时在线性基中求第 $k$ 小即可,单轮转移复杂度为 $O(n\log V)$,总时间复杂度为 $O(n\log^2V)$,有一些边界情况需要特殊处理。

D4T1 爬山

题意

有长为 $n$ 的山,每处有高度 $a_i$,初始 $a$ 不降。当处在第 $i$ 个位置时,可以将 $a_i$ 增加 $1$ 或将 $a_{i+1}$ 减少 $1$,只有在 $a_{i}=a_{i+1}$ 时才能从 $i$ 走到 $i+1$。现要连续爬 $k$ 次山,每次从 $1$ 开始走到 $n$,高度的变化会保留,求总共需要增加或减少的最少次数。$n,k\le2\times 10^5,a_i\le 10^9$。

题解

发现一定可以从某个位置将整个序列分成两半,其中前半部分每次走时只通过增高自身通向下一格,后半部分在第一次爬山中推平成相等,之后均直接走过去。设分界点为 $x$,则推平后半部分的代价为 $\sum_{i=x+1}^n a_i-a_x$,现在还要计算每个前缀在 $k$ 次爬山中的总贡献。

注意到这个前缀每次是整体左移一格,并把结尾复制了一份放在结尾。所以考虑一个 $a_i<a_{i+1}$ 的位置 $i$,在前 $i$ 次爬山时均存在这样的间隔,需要增高 $(a_{i+1}-a_i)$ 次以通过,之后该间隔就被右移没了。所以贡献为 $(a_{i+1}-a_i)\times \min(k,i)$,记录前缀贡献,与后缀代价拼起来取最小值即可。复杂度 $O(n)$。

D4T2 生成树

题意

给定 $K,V,E$,要求构造一张点数不超过 $V$,边数不超过 $E$ 的有向图,使得其中存在节点 $r$ 使得以 $r$ 为根的外向生成树个数恰好为 $K$。$V=80,E=220,K\le 10^{18}$。

题解

考虑把 $K$ 的二进制位拆出来分别构造。先连一条长为 $(\log K+1)$ 的有向链,以链头为 $r$。另开一个点 $T$,向链上所有除 $r$ 以外的点连有向边。此时从链尾向 $r$ 从 $0$ 开始标号,标号为 $i$ 的点只要向 $T$ 连一条有向边,就会对方案数产生 $2^i$ 的贡献。

由于可以用这样的边连接上 $T$,同时这些边只会选一条,所以相互独立。选择第 $x$ 条边后,所有 $i<x$ 的 $i$ 均可以从 $T$ 或 $(i+1)$ 连过来,贡献为 $2^i$。这样总点数为 $\log K +2$,总边数不超过 $3\log K$,符合题目要求。

D5T1 抽卡

题意

给定 $n\times m$ 的网格,其中有一些格子是关键格。每轮等概率选中一条两个格子之间的边,覆盖对应的两个格子,求期望多少轮能覆盖所有的关键格。$n\le 7,m\le 200$。

题解

我们发现要求的是 $\max_{x\in S} t_x$ 的期望值,其中 $t_x$ 表示 $x$ 被覆盖的时间。发现这个 max 很不好算,但如果是 min 的话,找出所有关键点周围的所有边,期望即为选中这些边之一的期望次数,每轮选中的概率为 $\frac {cnt_S}{sum}$,期望次数即为倒数 $\frac{sum}{cnt_S}$。那么通过 min-max 容斥即可得到 $\max_{x\in S} t_x=\sum_{T\subseteq S}(-1)^{\left|T\right|+1}\frac{sum}{cnt_T}$。

现在需要快速统计右面的值,也就是需要求出每种 $cnt_T$ 的点集选择方案的容斥系数之和。考虑轮廓线 DP,设 $f_{i,j,S,c}$ 表示考虑到 $(i,j)$ 格子,$S$ 中的所有为 $1$ 的行左边已选,在两边的格子都已经考虑过的边中有 $c$ 条边能覆盖到,这样的点集容斥系数之和。转移时枚举当前点是否选,多考虑上两条边并更新 $c$ 即可。最终对所有不同的 $c$ 用容斥系数统计答案,DP 状态数 $O(n^2m^22^n)$,转移 $O(1)$,总复杂度 $O(n^2m^22^n)$。

原题

数据弱化版:UOJ422

D5T2 分裂

题意

有一个 $4$ 行无穷列网格,初始只有一只生物在 $(0,0)$ 处。每次可以任选一只生物,使其消失并在 $(x,y+1)$ 和 $((x+1)\bmod 4,y+1)$ 处分别产生一只新生物,需要保证这两个格子里原来是空的。对于所有 $i\in[1,n]$,求分裂 $i$ 次后生物分布状态的方案数,对输入的模数取模。$n\le 10^6$。

题解

注意到如果有一个位置出现过 $4$ 次生物,或者同一列总共出现过 $8$ 个生物,那么后面一定会无限复制下去,一定不能满足题意。所以用四个值 $S=(a_0,a_1,a_2,a_3)$ 表示该列第 $i$ 行出现过 $a_i$ 次生物,然后 $2^4$ 枚举最终这一列是否保留,把剩下的推到后一行,这样就可以得到下一行各点被经过的次数 $T$,可以连一条从 $S$ 到 $T$,边权为该列操作次数的边。

如果把所有状态看做点,带权的转移看做边,那么 $i$ 的答案即为起点 $(1,0,0,0)$,终点 $(0,0,0,0)$ 且长为 $i$ 的路径数量。搜索可得从起点能到达的状态只有 $26$ 个,直接设 $f_{i,j}$ 表示从起点到第 $j$ 个状态长为 $i$ 的路径条数,暴力转移即可。时间复杂度为带常数 $26$ 的 $O(n)$。

D6T1 串

题意

给定两个长为 $n$ 的串 $S,T$,现选出一些位置,在两个串中同时将这些位置的字符删去,把剩下的字符拼接成 $S',T'$,求字典序最大的 $S'+T'$。$n\le 47$。

题解

考虑直接设 $f_i$ 表示目前保留 $i$ 个位置时字典序最大的 $S'+T'$,每次枚举所有 $f_i$ 并尝试转移给 $f_{i+1}$。注意比较大小时由于长度相同,可以直接比较,为了插入方便也可以开两个串分别记录 $S',T'$,依次比较即可。最后在所有 $f$ 中取字典序最大的即为答案,时间复杂度 $O(n^3)$。

D6T2 环

题意

有 $N=2A+B$ 根绳子,其中两端红和两端绿的绳子各有 $A$ 根,一端红一端绿的绳子有 $B$ 根,将所有红端点和绿端点随机匹配,即在 $N!$ 种方案中随机选择一种,把匹配的端点连接起来形成若干个环,求环个数的期望。$A,B\le10^6$。

题解

若 $B>0$,则取出一根红绿绳子,分讨其红色端点的连接情况:

  • 连接自己的绿色端点,环数增加 $1$。
  • 连接其他红绿绳子的绿色端点,环数不变,合成一根新的红绿绳子。
  • 连接绿绿绳子的某个端点,环数不变,合成一根新的绿绿绳子。

注意到三种操作都使得红绿绳子的数量减少了 $1$,其余不变,且只有第一种向答案贡献了 $1$。因此当 $B>0$ 时,可将答案增加 $\frac 1{2A+B}$,之后 $B$ 减少 $1$。这一部分贡献为 $\sum_{i=1}^B \frac 1{2A+i}$。

之后 $B=0$,则再取出一根红红绳子,其一个端点一定连接着某根绿绿绳子,所以把这两根绳子合成一根新的红绿绳子,也即 $A$ 减少 $1$,$B$ 增加 $1$,接着计算贡献直到 $A,B$ 均为 $0$ 即可,这部分贡献为 $\sum_{i=1}^A\frac 1{2i-1}$。两部分贡献加起来即为答案,时间复杂度 $O(A+B)$。

D6T3 树

题意

给定一棵树,点有点权,$q$ 次询问 $(x,k)$,找出 $x$ 子树内所有距离 $x$ 不超过 $k$ 的点,求这些点点权与其到 $x$ 距离的异或值的和。$n,q\le 10^6,a_i\le 10^9$。

原题 + 题解

P10107题解

D7T1 染色游戏

题意

有 $n\times n$ 的网格,初始全为白色。每次操作会把一整行全部覆盖成红色,或是把一整列全部覆盖成蓝色。已知每行每列都恰好被操作过一次,给出最终网格中每个格子的颜色,求有多少种操作方案可以得到这样的网格。$n\le 2000$。

题解

注意到每个格子只会被覆盖两次,且被行操作时会变成红色,被列操作时会变成蓝色,因此 $(i,j)$ 的最终状态就限制了第 $i$ 行和第 $j$ 列的操作顺序。把每行每列都建成一个点,每个格子的限制连成一条有向边,操作方案数即为这张图拓扑序的数量。

但是拓扑序计数做不了,注意到如果忽略边的方向,这是一张完全二分图。这也意味着两部分不可能同时存在没有入度的点。所以每次取出没有入度的一组点,它们的先后顺序任意,将答案乘上个数的阶乘并且全部删除,继续拓扑即可。时间复杂度 $O(n^2)$。

D7T2 01 序列

题意

给出一个长为 $n$,某些位置不确定的 $01$ 序列,不确定的位置在 $01$ 中等概率选择。设一个 $01$ 序列的权值为 $a\times b$,其中 $a$ 为最长不下降子序列长度,$b$ 为最长不下降子序列中 $1$ 的最大个数,求该序列权值的期望。$n\le 1000$。

题解

考虑一个 $01$ 序列如何求其权值,一定是找到一个分界点 $p$,选择其前面的所有 $0$ 和后面的所有 $1$,并且在保证长度最大的前提下使得 $1$ 的数量尽可能多。如果把 $0$ 看作 $-1$,再对原序列求前缀和后放到坐标系中,得到一张折线图。注意到取最低点为分界点时子序列一定最长,因为非最低点移动至最低过程中答案会增加。为了同时让 $1$ 最多,应该选择最靠左的最低点为分界点。

那么考虑记 $f_{i,j}$ 表示考虑了前 $i$ 个字符,目前纵坐标比前缀最低点高 $j$ 个单位的答案。但是这样转移很困难,所以考虑记录 $1,a,b,ab$ 四个值目前的和,其中 $1$ 即为到这个点的方案数。当加入 $1$ 时从 $f_{i-1,j}$ 转移到 $f_{i,j+1}$,其中 $a,b$ 均加了 $1$。加入 $0$ 时分有没有产生新的最低点讨论,若不产生则没有任何贡献,直接把 $f_{i-1,j}$ 累加给 $f_{i,j-1}$ 。若产生则只更新 $1$ 和 $a$,$b,ab$ 均变成 $0$ 所以不转移。这里 $a$ 还要增加 $1$,原因的话考虑从上个最低点到 $(i-1)$ 的 $01$ 个数相同,在状态的 $a$ 内统计了 $1$ 的贡献,只需要再加上最后一个 $0$ 即可。

最后统计所有 $f_{n,i}$ 的 $ab$ 的和,除以总方案数 $2^{cnt}$ 即为答案。时间复杂度 $O(n^2)$。

D7T3 区间

题意

给出一个长为 $n$ 的序列 $a$,对于 $x\in[1,k]$ 求有多少个区间 $[l,r]$ 满足 $\gcd(\max_{i=l}^ra_i,\min_{i=l}^ra_i)=x$。$n,k,a_i\le 2\times 10^5$。

题解

由于是最大值和最小值的关系,考虑单调栈或分治。这题可以单调栈 + 线段树做,但是分治做法更好想,常数也比较小。

考虑从 $[1,n]$ 开始分治,每次只统计跨过 $mid$ 的区间答案,然后分治到两边继续统计。分讨最大值位置,若最小值在同侧,则另一侧的长度有上界,直接将答案 $r$ 累加上长度个数即可。若最小值在异侧,则另一侧的长度由于最大值在另一侧有上界,由于最小值在该侧有下界。这时枚举最大值侧的长度,相当于要在另一侧维护一个可重集 $S$,支持动态增删点,同时给出 $x$ 后可以对于所有 $y\in S$,给 $c_{\gcd(x,y)}$ 加上 $1$。

考虑设 $c'x$ 表示 $x$ 为公因数出现了多少次,此时不一定是最小公因数,那么 $c_x=c'_x-\sum{y\mid x,y>x} c'_y$,维护 $c'$ 即可。考虑记数组 $t_d$ 表示目前 $S$ 中 $d$ 的倍数个数,插入或删除时枚举因数更新 $t$,查询时枚举所有 $x$ 的因数 $ d$ 给 $c'_d$ 加上 $t_d$ 即可。最后反演出 $c$ 后累加给 $r$ 即为答案。时间复杂度 $O(nd(n)\log n)$。

2.13T2 神庙

题意

给出一个排列 $p$,每个位置可以选择加上 $10^{100}$ 或不变,之后通过邻项交换使其变为升序,求加完后需要交换的最少次数。$n\le 2\times 10^5$。

原题 + 题解

需要转化的数据弱化版:AT_awtf2024_d题解

2.14T2 数位

题意

给出一个某些位不确定的 $01$ 串,填满后按顺序使用 $a_i$ 对 $x$ 进行操作,初始 $x=0$。操作为若 $a_i=0$,则 $x=x-f(x)$,否则 $x=x+f(2^k-1-x)$,其中 $f(x)$ 表示 $x$ 的 lowbit 值,$f(0)=0$。对所有 $i\in [1,n]$ 的 $i$,求有多少种填 $01$ 的方案使得 $s_i=0$,且最终 $x\le r$。$n\le 10^5,k\le 20$。

题解

首先考虑一个显然的暴力,设 $f_{i,j}$ 表示进行完前 $i$ 个操作后 $x=j$ 的方案数,可以直接转移。再设 $g_{i,j}$ 表示当前 $x=j$ 时,再进行 $[i,n]$ 中的所有操作后不超过 $r$ 的方案数,同样可以直接转移。对每个位置求答案时用 $f_{i-1}$ 和 $g_{i+1}$ 拼起来,中间进行一次 $a_i=0$ 的转移即可。时间复杂度为 $O(2^kn)$。

上述做法由于状态数就有 $nV$,转移也难以优化,所以考虑压缩状态数。发现可以记录 $x$ 和 $r$ 的 LCP 长度,这样通过第一个不同位即可区分大小。为了得知进行操作后的情况,还要记录 LCP 之后 $1$ 的个数,才能在操作时得知 LCP 的变化情况。据此设 $f_{i,j,k},g_{i,j,k}$ 分别表示 $x$ 与 $r$ 的 LCP 长为 $j$,LCP 之后还有 $k$ 个 $1$ 的所有 $x$ 的总方案数,其他含义与暴力相同。这里可以预处理 $to_{j,k,0/1}$ 表示转移到的 $j',k'$,此处有巨大分讨,其余做法不变,时间复杂度 $O(k^3+nk^2)$。

搬题

U535402

2.14T3 数论

题意

设 $f(p,q,r)$ 为方程 $px\equiv q\pmod r$ 的最小非负整数解,无解则为 $0$。$T$ 次询问给定 $n,a,b$,求 $\sum_{i=1}^n f(a,b,i)$,取模输出。$T\le 5,n\le 10^{18},a,b\le 10^6$。

题解

根据题意 $f(a,b,i)$ 即为 $ax+iy=b$ 中 $x$ 的最小非负整数解,此时若 $i>\max(a,b)$,则 $y=\frac{b-ax}i \le \frac bi<1$,因此 $y<0$。所以可以设 $z=-y$,原式即 $ax-iz=b,x=\frac {b+iz}a$。此时使 $x$ 取到最小非负整数的 $z$ 需要满足 $z$ 非负且 $b+iz$ 整除 $a$,也即 $iz\equiv -b\pmod a$,根据定义得 $z=f(i,-b,a)$。

显然 $f(p,q,r)=f(p\bmod r,q,r)$,那么 $\max(a,b)$ 及以下的直接暴力,$\max(a,b)$ 以上的按对 $a$ 取模的值分类,每一类的 $z$ 相同,则为一个等差数列,计算一下左右端点累加即可。求 $f$ 直接使用扩展欧几里得,时间复杂度 $O(V\log V)$,其中 $V$ 为 $a,b$ 的值域。

原题

Gym104053F

2.17T1 删树

题意

给定一棵 $n$ 个点的树,每次操作需要选择一部分点删去,要求操作后整棵树仍连通。对于 $k\in[1,n]$,求恰好 $k$ 次将整棵树删空的方案数。$n\le400$。

题解

考虑在固定根节点,并且要求每个点不晚于其父亲被删时求解。那么我们设 $t_u$ 表示 $u$ 点被删去的轮数,要求即为 $t_{u}\le t_{fa_u}$。这样的 $t$ 的方案数可以直接使用树形 DP + 前缀和优化求出。然而可能会出现某一轮没有节点被删的非法情况,需要容斥减去。这里设 $r'i$ 表示求出的方案数,那么 $r_i=r'_i-\sum{j=1}^{i-1} r_j\times {{i-1}\choose {j-1}}$,也即要减去实际上只有 $j$ 轮操作有点被删的方案数。

但是对于一种删除方案,所有在最后一次被删除的连通块内的点为根时均会被记录一次,为了统计最终答案,还需要进行点边容斥,也即对于每条边,将其两端点缩为一个点作为根,将其答案在最终答案中减去。这样每个连通块的点数与边数之差为 $1$,最终贡献即为 $1$,时间复杂度 $O(n^3)$。

如果要优化复杂度,考虑优化跑 $n$ 遍 $O(n^2)$ DP 的过程,也即通过换根降低复杂度。但是这个把两个点缩成一个后的 DP 难以换根,只能直接更换一种统计答案的方式。考虑钦定每个连通块中在以 $1$ 为根时深度最小的结点为代表,对于每个在最后删除的连通块只在其代表处统计答案。

这样就可以换根了,只要在换根时要求父亲删除的轮次小于该节点即可,仍然可以前缀和优化,统计答案同样容斥。另外换根时从父亲的状态中删去自身需要乘逆元,这个可以在 $O(n+\log n)$ 的复杂度内类似阶乘预处理,时间复杂度 $O(n^2)$。

搬题

数据加强版:U536130

2.17T2 移动点集

题意

给定数轴上 $n$ 个点的位置 $x_i$,对于每个点只能将其放在 $x_i-d$ 或 $x_i+d$,放完后需要用若干个区间覆盖所有点,一个区间 $[l,r]$ 的代价为 $a+b(r-l)$。求任意放置后覆盖所有点的最小代价。$n,x_i\le 100,d\le 50$。

题解

我们把位置要求变成 $x_i$ 或 $x_i+2d$,此时若 $d$ 比较小,可以状压 DP,设 $g_{i,S,0/1}$ 表示填到 $i$ 位置,最近的 $2d$ 个位置有无点的情况为 $S$,且 $i-2d$ 位置是否被覆盖时,$i-2d$ 及之前位置总花费的最小值。讨论一下转移即可,复杂度 $O(V4^d)$,其中 $V$ 为 $x_i$ 的值域。

另外还可以注意到模 $2d$ 同余的 $x$ 可以放到一起考虑位置,那么 $d$ 比较大时,每个同余类内的点数 $\frac V{2d}$ 就比较小了。那么此时考虑直接 $2^{\frac V{2d}}$ 枚举每个等价类内的状态,然后 DP 把等价类拼接起来即可。在 $S$ 后面拼上 $T$ 时两者中均为 $1$ 的位置贡献 $\min(a,b)$,只有 $T$ 为 $1$ 的位置贡献 $a$。另外第一个等价类除第一个外要接在最后一个之后,所以再枚举一下第一个等价类的状态再转移,复杂度会高一些,大概为 $O(8^{\frac V{2d}}d)$。

第一个做法大概可以做 $d\le 8$,那么 $d>8$ 时 $\frac V{2d}\le 7$,后一种做法可以通过,总复杂度不再叙述。

2.17T3 最小生成树

题意

给定 $(n+1)$ 个点的无向图,点从 $0$ 开始编号。对于 $i\in[1,n]$ 有一条从 $0$ 到 $i$,边权为 $a_i$ 的边,另有 $m$ 条连接 $u_i,v_i$,边权 $w_i$ 的边,这种边不会连接 $0$ 点。每次操作修改 $a_p$ 为 $x$,每次操作后询问该图最小生成树的边权和。$n,m,q\le 5\times 10^5$。

题解

考虑如果 $m=n-1$ 且恰好把 $n$ 个点连成一条链,那么考虑 DP,设 $f_{i,0/1}$ 表示考虑了前 $i$ 个点,且第 $i$ 个点是否与 $0$ 点连通的最小花费,转移为 $f_{i,0}=\min(f_{i-1,0}+b_i,f_{i-1,1}),f_{i,1}=\min(f_{i-1,0}+a_i+b_i,f_{i-1,1}+\min(a_i,b_i))$,其中 $a,b$ 分别是连到 $0$ 和 $i-1$ 的代价。注意到可以使用 min+ 矩阵刻画转移,所以放到线段树上维护 min+ 矩阵即可支持单点修改。

那么如果不是链,就先跑出 $n$ 个点的最小生成森林,并且建出 Kruskal 重构森林。考虑把这东西等价转化成一条链,那么直接按照其中序遍历访问初始点的顺序建成链,$b$ 的值赋为 $id_i$ 和 $id_{i-1}$ 在重构树上 LCA 的点权,若不在同一棵树上则为正无穷,否则在中序遍历到时赋值即可。这样等价的原因是如果跑 Kruskal,那么在目前访问到的边权上界相同时,新图和原图中所有点对之间的连通性相同,所以在这种含义下等价。那么就转化成了一条链的情况,时间复杂度为带矩阵乘法常数的 $O(n\log n)$。

原题

Gym102962E

2.19T1 序列

题意

给定长为 $n$ 的序列,$q$ 次询问,每次给出 $w$ 的初始值,之后依次遍历 $i\in [1,n]$,若 $w\le a_i$ 可以选择给 $w$ 异或上 $a_i$,求能异或上的数的最大个数。$n\le2\times 10^5,q\le 10^6,a_i,w< 2^{30}$。

题解

考虑找到 $w$ 的最高位,那么其一定能异或掉所有最高位低于其的数,同时与其最高位相同的数最多只能异或一个。所以答案为 $cnt$ 或 $cnt+1$,只需要对每个 $w$ 判断是否存在与其最高位相同的 $a_i$ 满足到 $i$ 时 $w\le a_i$,且异或上 $a_i$ 后还能把后面最高位低于 $w$ 的数全都异或上。

容易想到先根据 $w$ 的最高位分类,每一类分别判断。这时取出所有最高位低于 $w$ 的 $a_i$,对其求前缀异或和 $s_i$,那么若有最高位与 $w$ 相同的数 $a_x$,则满足 $w\oplus s_{x-1}\ge a_x$,且对于任意 $j>x$ 都有 $w\oplus a_x\oplus s_{j-1}\ge a_j$ 时 $w$ 便存在对应的 $a_x$,可以让答案加 $1$。

观察发现后面的 $O(n)$ 个限制有效的不多,因为从后往前非后缀最大值的限制一定可以删去,现在需要考虑相等情况。发现若存在 $p,q$ 使得 $a_p,a_q$ 均为后缀最大值,则 $w$ 异或完 $p$ 之前的元素后还需要异或上至少两个最高位与 $a_p$ 相同的,此时若 $w$ 高于 $a_p$ 则后面一定都能取完,否则异或 $a_p$ 后最高位下降,一定取不完。因此若出现两个相等的,直接将这两个限制保留并忽略之后的限制即可,这样总限制数为 $O(\log V)$ 级别。

注意到每个限制均形如 $w\oplus x\ge y$,而枚举 LCP 长度即可证明满足限制的 $w$ 在 Trie 树上为根深度不同的 $\log V$ 棵子树,那么 $O(\log V)$ 组 $\log V$ 棵子树的交形成了 $O(\log^2V)$ 棵子树,初始建出 $w$ 的 Trie 树并在上面打标记即可,时间复杂度 $O(n\log^2V)$ 或 $O(n\log^3V)$。

2.19T2 散步

题意

给定 $n$ 个二元组 $(a_i,b_i)$,定义长为 $n$ 的排列 $p$ 权值为最小的 $i\in [1,n]$ 使得 $\forall j\ge i,\sum_{k=i}^j(a_{p_k}-b_{p_k})\ge0$,且 $\forall j<i,\sum_{k=i}^n(a_{p_k}-b_{p_k})+\sum_{k=1}^j(a_{p_k}-b_{p_k})\ge 0$,若不存在则权值为 $0$。求所有 $n!$ 种排列的权值之和。$n\le 20$。

题解

注意到 $(a_i,b_i)$ 其实只会用到 $s_i=a_i-b_i$,题意中的限制其实相当于循环位移后的所有前缀和非负。此时若把 $s_i$ 的前缀和放到坐标系中,则 $i$ 对应的位置其实是最靠左的最低点。因为只有以最低点位移后的前缀和均非负,其中最小的 $i$ 即为最靠左的。那么从最低点向左即为一段始终为正的折线,向右即为一段始终非负的折线。

所以需要求出 $f_S,g_S$ 分别表示使用 $S$ 中的 $s_i$ 拼成始终为正或非负的折线方案数,枚举下一个选的段,判断合法并转移即可。这里需要注意 $f$ 是反着向左始终为正,也即 $s$ 的和始终为负。若设 $U$为全集,答案即为 $\sum_S f_S\times g_{U-S}\times{\left|S\right|}$。时间复杂度 $O(n2^n)$。

2.21T1 异或树

题意

给定一棵 $n$ 个点的树,点有点权,分别求树上点两两之间路径按位与、按位或、按位异或值平方的和。$n\le10^5,a_i< 2^{25}$。

题解

由于都是位运算,考虑拆位计算贡献。一个数可以表示为 $\sum_{k=1}^x 2^{p_k}$,其平方即为 $\sum_{k=1}^x\sum_{l=1}^x 2^{p_k+p_l}$,因此枚举 $p_k,p_l$,求有多少条路径的值中 $p_k,p_l$ 两位均为 $1$,累加贡献即可。只需要设 $f_{u,0/1,0/1}$ 表示从 $u$ 点向其子树内的链中,$p_k,p_l$ 两位分别为 $0/1,0/1$ 的链数量,直接转移并在 LCA 处统计贡献即可。

可以加一些常数优化,重要的是只跑 $p_k\le p_l$ 的部分,将 $p_k<p_l$ 的贡献翻倍即可。不太重要的还有计算与和或答案时可以少开一些之类,但是不加也能过。时间复杂度为 $O(n\log^2 V)$,带着不小的常数。

原题

QOJ5566

2.21T2 矩阵填数

题意

有一个 $n\times n$ 的矩阵,给出 $n$ 个限制 $(x,y,c)$,向矩阵内填数,要求 $a_{i,j}\ge a_{i,j+1}$ 且 $a_{i,j}\ge a_{i+1,j}$,求满足该要求的前提下 $\sum_{i=1}^n \left|a_{x_i,y_i}-c_i\right|$ 的最小值。$n\le 2\times 10^5$。

题解

显然若一个位置填的数不为某个 $c$,一定可以调整到 $c$ 且答案不增,因此先对 $c$ 离散化即可。考虑若 $c\in\{0,1\}$,那么 $1$ 和 $0$ 之间的分界线一定是一条从右上到左下的折线。考虑对这条线 DP,设 $f_{i,j}$ 表示考虑到第 $i$ 行,该行的分界线在第 $j$ 列处时的最小值,不妨认为初始所有点都在右下,那么左上的所有 $1$ 贡献 $-1$,所有 $0$ 贡献 $1$,如此可以做到 $O(n^2)$ DP。

由于整个矩阵中的值都是递减的,所以不同值的分界线一定是从左上到右下的若干条折线。考虑用一个整体二分来求出所有折线,取 $mid$ 后把目前剩余的所有限制分成最终值大于 $mid$ 和不大于 $mid$ 两部分,那么可以认为左上所有大于 $mid$ 的限制贡献 $-1$,所有不大于 $mid$ 的限制贡献 $1$,这样 DP 只需要还原一下方案,就可以把所有限制分成两半,递归下去整体二分即可,时间复杂度 $O(n^2 \log n)$。

注意到 DP 的操作为后缀加和对后缀取 min,因此维护 DP 值的差分数组,一个 $1$ 的贡献会直接加到差分数组里,一个 $-1$ 的贡献可以消去其前面或相同位置上的一个 $1$。用 set 维护所有 $1$ 的位置,过程中记录一下 $-1$ 所消去的 $1$ 的位置。由于需要方案,需要倒着还原 DP 数组,这里记录目前分界点 $p$,若出现 $-1$ 没有用过从而留在了 DP 数组中,或是消去了 $p$ 及其前面的一个 $1$,那么将 $p$ 后移至该 $-1$ 的位置一定不劣,于是我们做到了 $O(n\log n)$ DP。总时间复杂度为 $O(n\log^2n)$。

原题

QOJ8522

2.25T1 翻转

题意

定义长为 $n$ 的排列 $p$ 权值为:依次处理 $i\in[1,n]$,找到 $p_x=i$,然后翻转 $[x,n]$,权值为 $n$ 次翻转的区间长度之和。多次询问给出 $n$,要求构造权值尽可能小的 $p$。评分标准为设 $P$ 为给出排列的权值,$q=n\log_2 n+\lfloor \frac n{12}\rfloor +5$,若 $P>2Q$ 则不得分,否则每个点的分值 $W=20$,得分为 $W\min(1,1-\log_2(\frac PQ))$,四舍五入取整。$T\le 10,n\le 10^5$。

题解

首先注意到把 $1$ 放到结尾一定不劣,但是没什么用。再注意到长为 $n$ 的排列可以从 $mid$ 处分为两部分,后半部分直接递归下去构造 $\lfloor\frac n2\rfloor$ 的答案,再将下一个数放到序列开头,从而在下一次操作中把前半部分翻转到后半部分。注意到这个数翻到了最后,也即 $1$ 应该在的位置,所以直接递归构造 $\lceil \frac n2\rceil$ 的答案并翻转一下即可。

求权值可以直接暴力求,也可以递推全部预处理出来。这样构造的排列权值 $P$ 最多会比 $Q$ 大大约 $400$,然而四舍五入后可以得到满分。时间复杂度 $O(n\log n)$。

2.25T2 编号

题意

定义长为 $n$ 的排列 $p$ 权值为:将其分成极长值域上连续段,权值为这些段的最大长度。求 $n!$ 种排列的权值之和,对给定的模数取模。$n\le 3000$。

题解

若把值域分成 $i$ 段,那么这 $i$ 段之间的排列方式需要满足第 $j$ 段不在第 $(j-1)$ 段后面相邻。可以设 $g_{i,j}$ 表示放了前 $i$ 段,且 $(i-1)$ 个相邻对中有 $j$ 个不合法的方案数,那么合法排列方式数即为 $g_{i,0}$,$O(n^2)$ 即可 DP 预处理求出。

那么还需要求把值域分成 $i$ 段,且最长段长度为 $j$ 的方案数。直接容斥 + 隔板法,钦定 $k$ 段长度至少为 $j$,剩余长度再组合数分。同时由于已经钦定过的可以为空,而没钦定过的不能为空,可以先给钦定过的减少 $1$,转化为全部不能为空的情况。因此该方案数为 $\sum_{k=1}^{\min(i,\lfloor\frac nj\rfloor)} (-1)^{k+1}\times {i\choose k} \times {n-(j-1)k-1\choose i-1}$。再乘上 $g_{i,0}$ 累加给答案即可。时间复杂度为 $O(n^2\ln n)$,来自枚举 $k$ 的调和级数。

P8334 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-05-09 18:01:18

题解好像大部分是差分拆贡献,这是 SD 二轮省集 Harry27182 老师的做法,感觉很牛,在此记录。

注:以下用 $w(x,y)$ 代表原题中的 $f(x,y)$,$mn_u$ 表示 $u$ 子树内 $a$ 的最小值。

显然可以先把 $a$ 离散化,之后考虑对最终答案相同的方案整体计算贡献。设 $f_{u,i}$ 表示对于所有合法的 $w(u,x)$,它们中值为 $i$ 的期望个数。转移时讨论子树内路径最小值是否仍为最小,乘上组合数转移。

具体地,对于 $u$ 的儿子 $v$,若以 $v$ 为起点时的最小值 $i$ 仍最小,需要 $u$ 其他子树中 $mn_x\le i$ 的所有 $x$ 在搜索时均排在 $v$ 之后。此处计算方案数时,可设 $u$ 共有 $S$ 个儿子,则先拿出 $mn_x\le i$ 的 $c$ 个(其中必然包含 $v$),要求 $v$ 在开头,其他随意排列,剩余的 $(S-c)$ 个也随意排列。之后将 $S$ 个位置分类并分别放入,即为合法排列数。因此概率为 $$ \frac{{S\choose c}\times (c-1)!\times (S-c)!}{S!}=\frac{S!}{c!\times (S-c)!}\times (c-1)!\times (S-c)!\times \frac 1{S!}=\frac 1 c. $$ 转移即为 $f_{u,i}\leftarrow f_{v,i}\times\frac 1 {\sum_{x\in son(u)}[mn_x\le i]}$。

另一种情况是最小值变成了另一子树 $x$ 的最小值 $mn_x$,最终结束于 $v$ 子树内的终点,这里显然有限制 $mn_x\le i$。此时先将 $u$ 的所有子树按 $mn$ 从小到大排序,设 $rk_x$ 为排序后 $x$ 的排名。则所有排在 $x$ 前面的子树中,除 $v$ 外的其他子树必须排在 $v$ 之后。

那么需要分 $v$ 在 $x$ 之前和之后讨论,以确定需要在 $v$ 之后的子树个数。这里以 $v$ 在 $x$ 之前为例,仿照上面可以把 $rk_x$ 个子树拿出来单独排列,并将 $x,v$ 分别放到前两位,概率即为 $$ \frac{{S\choose rk_x}\times (rk_x-2)!\times (S-rk_x)!}{S!}=\frac{S!}{rk_x!\times (S-rk_x)!}\times (rk_x-2)!\times (S-rk_x)!\times \frac 1{S!}=\frac 1 {rk_x(rk_x-1)}. $$ 同理可得 $v$ 在 $x$ 之后时系数为 $\frac 1{rk_x(rk_x+1)}$。转移时为降低复杂度,可以枚举 $x$,从而用前缀和优化省去对 $v,i$ 的枚举,即 $$ f_{u,mn_x}\leftarrow\frac 1 {rk_x(rk_x-1)}\sum_{rk_v<rk_x}\sum_{i\ge mn_x} f_{v,i}+\frac 1{rk_x(rk_x+1)}\sum_{rk_v>rk_x}\sum_{i\ge mn_x} f_{v,i}. $$

这两种转移完成后,由于从 $u$ 出发必然经过 $a_u$,需要把所有 $f_{u,i}$ 的 $i$ 对 $a_u$ 取 min,即将大于 $a_u$ 的 DP 值均加到 $f_{u,a_u}$ 上并清空。最后给答案加上所有 $w(u,x)$ 的贡献,即 $\sum i\times f_{u,i}$。

另外注意到会有相等的 $a$ 值,这时钦定 $f_{v,i}$ 中的 $i$ 为等大的数中最大的,其余相等的 $mn_x$ 中 $rk$ 在前的较小,这样定义后整个 DP 过程即上述,可以实现不重不漏。目前时间复杂度为 $O(n^2)$。

考虑优化 DP 过程,注意到第一种转移是对应位置累加,且每个位置上转移系数相等,可以使用线段树合并。同时由于转移系数只与不超过 $i$ 的 $mn_x$ 个数有关,不同的区间只有 $O(deg_u)$ 个,可以用区间乘解决。

进行第二种转移时,可以开一棵临时的线段树,通过合并得到 $rk$ 数组上前后缀的线段树,再进行 $i\ge mn_x$ 的区间查询,最后进行单点加即可。注意两者的系数不同,需要分别顺序和逆序做。最后对 $a_u$ 取 min 只需区间查询,清空即为区间乘 $0$,也是区间乘。

所以需要实现线段树合并,并支持区间乘,单点加,区间求和,这些操作均不难实现,时空复杂度 $O(n\log n)$。由于有区间乘和临时的前后缀线段树,最终空间大概需要 $4$ 倍的 $n\log n$,$3\times 10^7$ 就足够了。

附上代码:

#include<iostream>
#include<vector>
#include<algorithm>
#define pb push_back
#define mid ((l+r)>>1)
using namespace std;
const int N=4e5+10;
const int P=4e5;
const int M=3e7+10;
const int mod=998244353;
void add(int &a,int b) {a+=b;if(a>=mod)a-=mod;}
int n,m,rot,res,a[N],b[N],x[N],mn[N],rt[N],inv[N];
vector <int> e[N];
bool cmp(int i,int j) {return mn[i]<mn[j];}
struct sgmtt
{
	int t,lc[M],rc[M],w[M],k[M],tag[M];
	void cle(int u) {lc[u]=rc[u]=w[u]=k[u]=0,tag[u]=1;} 
	void pushup(int u) {w[u]=w[lc[u]],k[u]=k[lc[u]],add(w[u],w[rc[u]]),add(k[u],k[rc[u]]);}
	void pt(int u,int x) {w[u]=1ll*w[u]*x%mod,k[u]=1ll*k[u]*x%mod,tag[u]=1ll*tag[u]*x%mod;}
	void pushdown(int u) {if(tag[u]!=1) pt(lc[u],tag[u]),pt(rc[u],tag[u]),tag[u]=1;}
	void update(int u,int l,int r,int L,int R,int x)
	{
		if(!u||L>R) return;
		if(l>=L&&r<=R) {pt(u,x); return;}
		pushdown(u);
		if(L<=mid) update(lc[u],l,mid,L,R,x);
		if(R>mid) update(rc[u],mid+1,r,L,R,x);
		pushup(u);
	}
	void change(int &u,int l,int r,int p,int x)
	{
		if(!u) u=++t,cle(t);
		if(l==r) {add(k[u],x),w[u]=1ll*k[u]*b[l]%mod; return;}
		pushdown(u);
		if(p<=mid) change(lc[u],l,mid,p,x);
		else change(rc[u],mid+1,r,p,x);
		pushup(u);
	}
	int query(int u,int l,int r,int L,int R)
	{
		if(!u||L>R) return 0;
		if(l>=L&&r<=R) return k[u];
		pushdown(u); int tr=0;
		if(L<=mid) add(tr,query(lc[u],l,mid,L,R));
		if(R>mid) add(tr,query(rc[u],mid+1,r,L,R));
		return tr;
	}
	int merg(int u,int v,int l,int r)
	{
		if(!u||!v) return u+v;
		int p=++t; cle(t);
		if(l==r) w[p]=w[u],k[p]=k[u],add(w[p],w[v]),add(k[p],k[v]);
		else pushdown(u),pushdown(v),lc[p]=merg(lc[u],lc[v],l,mid),rc[p]=merg(rc[u],rc[v],mid+1,r);
		if(l<r) pushup(p);
		return p;
	}
}T;
void dfs(int u,int fat)
{
	mn[u]=a[u]; vector <int> p;
	for(int v:e[u]) if(v!=fat)
	{
		dfs(v,u),p.pb(v);
		mn[u]=min(mn[u],mn[v]);
	}
	sort(p.begin(),p.end(),cmp);
	int s=p.size(),cur=0;
	for(int i=0;i<s;i++)
	{
		x[i]=1ll*T.query(cur,1,m,mn[p[i]],m)*inv[i]%mod*inv[i+1]%mod;
		cur=T.merg(cur,rt[p[i]],1,m);
	}
	cur=0;
	for(int i=s-1;~i;i--)
	{
		add(x[i],1ll*T.query(cur,1,m,mn[p[i]],m)*inv[i+1]%mod*inv[i+2]%mod);
		cur=T.merg(cur,rt[p[i]],1,m),rt[u]=T.merg(rt[u],rt[p[i]],1,m);
	}
	for(int i=1;i<s;i++) if(mn[p[i]]!=mn[p[i-1]]) T.update(rt[u],1,m,mn[p[i-1]],mn[p[i]]-1,inv[i]);
	if(s) T.update(rt[u],1,m,mn[p[s-1]],m,inv[s]);
	for(int i=0;i<s;i++) T.change(rt[u],1,m,mn[p[i]],x[i]);
	T.change(rt[u],1,m,a[u],T.query(rt[u],1,m,a[u]+1,m)+1),T.update(rt[u],1,m,a[u]+1,m,0),add(res,T.w[rt[u]]);
}
void sol()
{
	cin>>n>>rot,res=T.t=0;
	for(int i=1;i<=n;i++) cin>>a[i],b[i]=a[i],rt[i]=0,e[i].clear();
	sort(b+1,b+1+n),m=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+m,a[i])-b; 
	for(int i=1,u,v;i<n;i++) cin>>u>>v,e[u].pb(v),e[v].pb(u);
	dfs(rot,0),cout<<res<<'\n';
}
int main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	inv[0]=inv[1]=1;
	for(int i=2;i<=P;i++) inv[i]=1ll*(mod-mod\/i)*inv[mod%i]%mod;
	int TT; cin>>TT;
	while(TT--) sol();
	return 0;
}

25.05-二轮省集模拟赛题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-05-12 19:06:19

由于水平有限,只补了一部分题,因此只有这部分题解。

D0T1 A

题意

给定无向连通图,求有多少种加边方案使得最终的图无重边、自环,且边双连通。取模。$n\le 5000,m\le 10^6$。

题解

边双连通即为不存在割边,因此先 Tarjan 跑出原图的边双连通分量并缩点,得到一棵由割边连接的树。此时边双内部的边可以任意添加,最后给答案乘上 $2^{cnt}$ 即可。问题转化为给新树加边,要求每条树边均被某条新边 $(u,v)$ 所对应的树上路径 $u\to v$ 覆盖,求方案数。注意此时每个新点内部有 $w_i$ 个点可向外连边,$w_i$ 为该边双的点数。

考虑容斥,枚举没被覆盖的边数 $i$,设方案数为 $g_i$,答案即为 $\sum (-1)^i f_i$。注意到钦定若干条边不覆盖后,整棵树被分成了若干连通块,每个连通块内可以任意连边,因此树形 DP 计数。最朴素的 DP 状态为 $f_{u,j,k}$ 表示 $u$ 子树内断了 $j$ 条边,$u$ 所在连通块大小为 $k$ 的方案数,转移类似树形背包。时空复杂度 $O(n^3)$。

但是注意到多断一条边时,容斥系数只会取反,而具体有多少条边并不重要。所以可以去掉边数一维,并将容斥系数加入 DP 值转移。具体地,设 $f_{u,i}$ 表示 $u$ 子树内 $u$ 所在连通块大小为 $i$ 时,子树内带容斥系数的方案数总和。初值为 $f_{u,w_u}=1$,转移时将子树 $f_v$ 合并到 $f_u$,若断边则 $f_{u,i}\leftarrow -\sum f_{v,j}$,容斥系数取反,点数不变;不断边则 $f_{u,i}\leftarrow f_{u,i-j}\times f_{v,j}\times 2^{j(i-j)-1}$,即 $u,v$ 两点间除树边外的所有边状态任意。树形背包转移即可,注意上下界优化,时空复杂度 $O(n^2+m)$。

D0T2 B

题意

给定一张 DAG,每条边是黑色或白色,$q$ 次询问当黑白边权分别为 $a,b$ 时,$1\to x$ 的最短路长度。$n,q\le 5\times 10^4,m\le 10^5$。

题解

考虑处理出 $1$ 到每个点的所有可能的 $(x,y)$,其中 $x,y$ 分别表示经过的黑白边数,询问时在每个点上枚举。若对每个 $x$ 保留最小的 $y$,复杂度为 $O((n+m+q)n)$。

然而注意到若把所有 $(x,y)$ 放到坐标系内,则可能成为最优解的 $(x,y)$ 一定在下凸包上。又有据说经典的 Trick,两维值域均为 $[1,n]$ 内整数的凸包点数至多为 $n^{\frac 23}$,因此在每个点上维护凸包,转移时暴力合并。如果建凸包时使用快排的话,复杂度为 $O(n^{\frac 53}\log n+(q+m)n^\frac23)$,然而远卡不满,容易通过。

D0T3 C

题意

定义序列的权值为通过邻项交换使其变为单峰序列的最少操作次数。给定排列 $a$,求 $a$ 每个前缀的权值。$n\le5\times 10^5$。

题解

先考虑单个序列的权值求法。首先关注最小值,其一定会被放在序列开头或结尾,同时归位后其余元素相对顺序不变。因此放在两处都会得到长度减少 $1$ 的子问题,对每个值依次求贡献即可。最终答案为 $\sum_{i=1}^n\min(\sum_{j<i}[a_j>a_i],\sum_{j>i}[a_j>a_i])$。

对每个前缀求解时,仍然考虑计算每个数的贡献。对于 $a_i$,前面的 $\sum_{j<i}[a_j>a_i]=c_i$ 始终不变,后一个数单调不降,讨论二者大小关系即可。具体地,在 $\sum_{j>i}[a_j>a_i]\in[0,c_i)$ 时若在末尾加入一个大于 $a_i$ 的数,则贡献加 $1$;不小于 $c_i$ 时贡献一直为 $c_i$,不再改变。因此找到 $a_i$ 之后第 $c_i$ 个大于 $a_i$ 的数 $a_{p_i}$,则 $(i,p_i]$ 内每次加入一个大于 $a_i$ 的数都会使答案加 $1$。

具体实现上先使用 BIT 求出 $c_i$,然后按 $a$ 的值从大到小排序,用权值线段树上二分求出对应的 $p_i$。最后增量维护答案,用 BIT 维护目前贡献还在增加的所有 $a_i$,长度增大时查询小于加入值的个数并加入答案,在对应的 $a_{p_i}$ 加入后从 BIT 中删除即可。时间复杂度 $O(n\log n)$。

D1T1 花卉港湾

题意

给定一棵树,建一张新图,给所有原树上距离不低于 $k$ 的点对间连边,$q$ 次询问新图上 $u,v$ 两点间的最短路。$n,q\le 10^5,1\le k<n$。

题解

首先若 $d(u,v)\ge k$,答案为 $1$。再分别找到距离 $u,v$ 最远的点 $p,q$,根据直径的性质可知 $p,q$ 均为直径端点。因此直接找出一条直径 $L\to R$ 用于判断。此时若 $u,v$ 中有一点无法到达直径端点,或直径长度不足 $k$,答案为 $-1$。否则一定能通过直径在 $3$ 步之内抵达,答案至多为 $3$。

之后只要解决答案为 $2$ 的判断就做完了。首先若有一个直径端点到 $u,v$ 距离均不低于 $k$,则可以其为中转点两步抵达。之后设 $p_i$ 表示直径上距离 $i$ 最近的点,$d_i$ 表示该距离,$w_x$ 表示 $\max_{p_i=x}d_x$,也即直径上的 $x$ 点子树内的最远距离。令 $p_u\le p_v$,则只有 $p_u<p_i<p_v$ 的点 $i$ 作为中转点时可能比两个直径端点优,根据直径的性质容易证明。此时要求 $x=p_i$ 满足 $x-p_u+w_x+d_u\ge k,p_v-x+w_x+d_v\ge k$,整理得 $w_x+x\ge k+p_u-d_u,w_x-x\ge k-p_v-d_v$,再加上 $p_u<x<p_v$,是三维偏序问题。

这样已经可以随便上点东西维护了,但是分讨一下可注意到若 $x$ 不满足 $p_u<x<p_v$,却满足另外两个限制,则某个直径端点一定能作为中转点,因此这个限制可以忽略。同时由于每个点都能贡献到每个询问,且只关心是否存在,而不关心具体个数,可在所有查询前用下标为 $w_x+x$ 的数组记录下对应的 $\max(w_x-x)$,并预处理出后缀最大值,查询可以做到 $O(1)$。时间复杂度为 $O(n\log n)$,来自求两点距离时的 LCA,因此用科技可以做到线性。

D2T1 MEX 求和

题意

给定非负序列 $b$,求 $\sum_{\forall i\in[1,n],a_i\in [0,b_i]}w(a)$,其中 $w(a)$ 表示 $a$ 数组的 MEX 值。$n\le 5000,b_i\le {10}^9$。

题解

考虑将 MEX 值拆成 $\sum_{i=0}^{n-1} f(x)$,其中 $f(x)$ 为 $1$ 表示 $a$ 中存在 $[0,x]$ 中的所有数,问题变为每个 $f(x)$ 能贡献的方案数。考虑先将 $b$ 从小到大排序,之后设 $f_{i,j}$ 表示填了前 $i$ 个数,此时不大于 $b_i$ 的数中还有 $j$ 个需要存在却还没有的方案数。那么从 $f_{i-1}$ 转移到 $f_{i}$ 时,又会多出 $m_i=\max(0,\min(a_i,x)-a_{i-1})$ 个数需要填,转移为 $f_{i,j+m_i}\leftarrow f_{i-1,j}\times (b_i-(j+m_i))$ 和 $f_{i,j+m_i-1}\leftarrow f_{i-1,j}\times (j+m_i)$,分别表示是否新填了一个需要填的数,总时间复杂度 $O(n^3)$。

这个状态的 $j$ 维含义比较抽象,因为这是赛时更换状态以图优化的结果。此时注意到可以分讨 $a_i$ 和 $x$ 的大小关系来确定 $m$ 的取值,即若 $a_i<x$,则 $m_i=a_i-a_{i-1}$;否则 $m_i=\max(0,x-a_{i-1})$,再分讨若 $a_{i-1}>x$,则 $m_i=0$,否则 $m_i=x-a_{i-1}$。注意到 $a_{i-1}<x<a_i$ 的位置只有一个,而其他情况下 $m_i$ 均与 $k$ 无关,因此用前后缀的 DP 数组预处理转移系数,每枚举一个 $x$ 暴力进行中间一个位置的转移即可,时空复杂度 $O(n^2)$。

D3T1 重建:地下铁道

题意

给定 $n$ 个点的环,每条边有长度,可任意定向。$m$ 个任务要求沿着边的方向从 $s_i$ 走到 $t_i$,代价为 $w_iL$,$L$ 为路径长度。求最小总代价。$n,m\le 5\times 10^5$。

题解

考虑先钦定 $(1,n)$ 之间边的方向,这里令其为 $n\to 1$,则 $s_i<t_i$ 的任务方向固定。对于 $s_i>t_i$ 的任务,先令其经过 $n\to 1$ 的边,考虑选择其中一些反向以最小化代价。首先若与 $s_i<t_i$ 的任务有相交的边,则一定不能反向。又注意到若有 $s_x>t_x$ 的任务不反向,则只有 $s_x\le s_i<t_i\le t_x$ 的任务 $(s_i,t_i)$ 才能反向。因此若有长为 $L$ 的任务未反向,则长度不低于 $L$ 的任务一定无法反向。这也说明将可反向的任务按长度排序,最终选择反向的一定是一段前缀中的任务。

因此排序后维护前缀所有任务 $[t_i,s_i]$ 的并,要求对应后缀所有任务的交包含该区间,对每个合法前缀求最小值即为答案。至于再钦定 $1\to n$ 时计算方式相同,可以将所有边和任务反过来,即将 $w$ 的 $[1,n-1]$ 翻转,每个任务变为 $(n-s_i+1,n-t_i+1)$,重做一遍即为 $1\to n$ 的答案。时间复杂度为 $O(m\log m)$,来自对区间进行排序,用桶排可以做到线性。

D4T1 崩坏世界的歌姬

题意

给定整数 $n,m$,计算有多少长为 $n$ 的排列满足任意相邻位置之和均不为 $m$ 或 $m+1$。$n\le 10^9,m\le 10^7$,其中 $80\%$ 满足 $m\le 3000$。

题解(80 分)

注意到将所有不能相邻的数之间连边后,$[1,m]$ 的数连成了一条链。因此想到先将 $m$ 个数排好,再向其中插入剩余的 $(n-m)$ 个数。注意到排完 $m$ 个数后,只关心中间的 $(m-1)$ 个空是否合法,从而确定每个空中是否可空,从而用插板法计算出方案数。因此只需要对所有 $j\in[0,m)$ 求填完后有 $j$ 个非法相邻的方案数。

考虑用 DP 解决该问题,设 $f_{i,j,0/1}$ 表示填了前 $i$ 个数,有 $j$ 个非法相邻,其中是否包含 $i$ 和 $i-1$ 的方案数。转移时分讨插入了 $i$ 和 $i-1$ 之间/合法空/非法空,乘上对应的个数转移即可,此处略去。算出 $g_{j}=f_{m,j,0}+f_{m,j,1}$ 后,即需要向 $(m+1)$ 个空中插入 $(n-m)$ 个数,其中 $j$ 个不可空,得到方案数为 ${n-x}\choose m$,还要乘上 $(n-m)!$,得到答案为 $\sum_{x=0}^{\min(m-1,n-m)}g_x\times \frac{(n-x)!(n-m)!}{m!(n-x-m)!}$。

另外需要求 $(n-i)!$,其中 $n\le 10^9,i\le 6000$,只能使用分块打表预处理出这些阶乘的值。总时间复杂度 $O(P+m^2)$,$P$ 是分的块长。另外本题还有 $20$ 分分别为 $m\le 10^5$ 和 $m\le 10^7$,分别是多项式优化和数学推递推式,不是很懂,不管了。

D4T3 每个人的结局

题意

给定平面内 $n$ 个矩形,可在平面内随意放三个点,$w_i$ 可以取 $x$ 当且仅当第 $x$ 个点在第 $i$ 个矩形内,求合法的 $w$ 序列数量。$1\le lx_i,rx_i,ly_i,ry_i\le n\le 2\times 10^5$,其中 $15\%$ 满足 $\max \{lx_i\}\le \min\{rx_i\}$ 或 $\max \{ly_i\}\le\max \{ry_i\}$。

题解(15 分)

注意到 $15\%$ 的特殊性质中有一维没用,因为只要将点放在 $[\max \{l\},\min\{r\}]$ 内,就能满足所有矩形这一维的限制。 所以变成了一维问题,即只有 $[l_i,r_i]$ 内包含第 $x$ 个点,$w_i$ 才能取 $x$。考虑找到右端点最小的区间和左端点最大的区间,设最小右端点为 $p$,最大左端点为 $q$。若这两个区间有交,即 $p\ge q$,则存在点被所有区间包含,答案为 $3^n$;否则两个区间的值一定不同,我们令其分别为 $1,2$,最终答案乘上 $6$ 即可。

注意到 $1,2$ 分别放在 $p,q$ 能在合法前提下覆盖到最多的区间,然后将 $3$ 放到 $(p,q)$ 开区间内 $pos$ 处。考虑每个区间的取值。设其能覆盖 $p,q$ 中 $k$ 个点,则若 $l_i\le pos\le r_i$,该区间有 $(k+1)$ 种取值,否则有 $k$ 种取值。 此时计算每个点作为 $pos$ 时的方案数:若 $k=0$,将区间外赋成 $0$;若 $k=1$,给区间内乘上 $2$;若 $k=2$,给区间内乘上 $3$,此时由于区间外一定在 $(p,q)$ 外,可以不用管。

再考虑统计最终答案,考虑使用类似点边容斥的 Trick,即对于某种方案有一段区间的点合法,则用这段区间的点数减去边数得到 $1$ 的系数。因此把点和边作为线段树下标,维护区间乘操作,最后求所有数带系数的和即为答案。时间复杂度 $O(n\log n)$。

至于满分做法需要在二维上钦定 $1,2$ 在两个角上后分讨 $3$ 的位置,再进行点边块容斥,扫描线维护。有点太复杂了,摆了,但感觉思路很有参考价值,故记录。

D5T1 互质序列

题意

给定 $n$,长为 $m$ 的合法序列要求 $a_i\mid n$,且相邻两数均不互质。$q$ 次询问长为 $m$ 的合法序列数量。 $n\le 10^{16},m\le 10^{18},q\le 150$。

题解

先将 $n$ 分解质因数,并求出其所有因数,若暴力则可以设 $f_{i,j}$ 表示填了 $i$ 个数,最后一个数为 $j$ 的方案数。若 $j$ 有 $V$ 种取值,则暴力转移复杂度为 $O(mV)$,矩阵快速幂优化复杂度为 $O(q\log m V^3)$,直接把所有因数放到 $j$ 上则有 $V_1=d(n)$。考虑优化 $V$ 的大小以降低复杂度,首先注意到每个质因子的个数不重要,只关心是否存在,因此可以压成 $01$ 串,做到 $V_2=2^{w(n)}$,其中 $w(n)$ 表示 $n$ 的质因子数量。

再注意到若把 $01$ 串所有质因子在 $n$ 的分解中最高次项取出来构成可重集,则若两个 $01$ 串 $x,y$ 对应的可重集相同,则 $\forall i,f_{i,x}=f_{i,y}$,因为这两个状态本质上是等价的。由此可以进一步压缩状态,用 $V_2$ 个状态预处理一下转移系数,最终得到 $V_3\le 250$,具体多少不清楚。

好像复杂度有点大,但是多次询问矩阵快速幂有经典优化,即先预处理出转移矩阵所有的 $2^k$ 次方矩阵,每次询问进行 $O(\log m)$ 次向量乘矩阵即可。最终复杂度为 $O(V_3^3\log m+qV_3^2\log m)$,可以通过。

D5T2 树的搜索

题意

设 $f(x,y)$ 表示从树上 $x$ 点开始,只向儿子方向随机 DFS(即每次在未访问过的儿子中等概率随机一个访问),访问到 $y$ 时经过的点权最小值的期望值,要求 $x$ 是 $y$ 的祖先。给定一棵树,求所有合法 $(x,y)$ 的 $f(x,y)$ 之和。$n\le 5\times 10^5,a_i\le 10^9$。

题解

原题是 P8334,这是题解

D6T1 构造题

题意

设 $f_{i,j}$ 表示从 $(1,1)$ 走到 $(i,j)$ 的路径数,要求在网格中选择一些位置标记为可通过,再选择若干可通过的格,使得它们的 $f$ 值之和为给定的 $k$。$q$ 次询问,要求所有询问使用相同的标记网格,标记的格数不超过 $X$,每次选择的格数不超过 $Y$。$q\le 10^4,X\ge 960,Y\ge 240,k\le 10^{100}$。

题解

先考虑使用二进制来刻画每一位,发现精细实现也需要构造到 $2^{330}$,即需要 $993$ 个格才能满足题意。尝试 $3,5$ 进制后发现均不如二进制优,但需要选择的格数变少了,可以通过一些部分分。

注意到使用其他进制时,$960$ 格的总和都难以达到 $10^{100}$。因此考虑先将总和达到上界,发现斐波那契数列增长较快,并且可以用两格的代价变为下一项。计算一下发现 $f_{480}$ 恰好能达到 $9\times 10^{99}$,总和已经可以达到上界。另外又有定理为每个自然数均能唯一地被互不相邻的若干项斐波那契数之和表示,赛时感性理解也想到了这一点,因此标记格数为 $\log_{\phi}k$,所选格数不超过 $\frac{\log_{\phi}k}2$,均满足要求。时间复杂度在高精度加法,需要使用压位高精。

D6T2 匹配题

题意

给定长为 $n$ 的字符串 $S$,要求从中删掉 $k$ 个位置,并将剩下的位置两两匹配,$i,j$ 匹配要求 $S_i\ne S_j$,代价为 $100\times\left|i-j\right|+w_i+w_j$。求匹配总代价最小值,若无合法匹配则输出 $-1$。多测,$T\le5,n\le 2000,k\le 6,w_i\le 10^5,S_i\in\{a,b,c,d,e,f\},n\equiv k\pmod 2$。

题解

考虑枚举分界点 $p$,将整个串分为 $[1,p],[p+1,n]$ 两部分,注意到两部分可以内部匹配,直到其中至少有一部分剩余字符全部相同。证明可以考虑调整,即若目前两边均剩余了超过一种字符,则找到在单侧数量最多的一种字符,令该侧最多和次多匹配一个,一定不会影响匹配的合法性。同时考虑到这样减少了跨过分界点的匹配数量,一定比不匹配优。

因此对于每个前缀,匹配后剩余的字符要么是同一种,要么会与之后的同一种字符匹配。据此设计 DP 状态 $f_{i,j,k,c,0/1}$ 表示考虑了长为 $i$ 的前缀,剩余 $j$ 个字符,删了 $k$ 个字符,剩余字符均为 $c$ 或均与后面的 $c$ 匹配的最小代价,需要将匹配代价拆到两个字符处以方便转移,即 $i<j$ 时 $i$ 处有 $w_i-100i$ 的代价,$j$ 处有 $w_j+100j$ 的代价。

转移讨论向前或向后匹配,根据 $S_i$ 是否为 $c$ 转移即可。需要格外注意的是 $0$ 状态可以在任意情况下直接转到 $d\ne c$ 的 $f_{i,j,k,d,1}$,而变为 $0$ 状态则需要 $j=0$。转移式此处略去,时间复杂度 $O(Tn^2kV^2)$,优化一下也可以做到 $O(Tn^2kV)$,其中 $V$ 为字符集大小。使用滚动数组优化后空间复杂度为 $O(nkV)$。

D7T1 宿雾若水遥

题意

给定一棵 $n$ 个点的树和 $m$ 条树上的链,$q$ 次询问给出 $L,R$,求 $\sum_{L\le l\le r\le R} w(l,r)$,其中 $w(l,r)$ 表示第 $l$ 条链到第 $r$ 条链的并集大小。$n,m\le 2\times 10^5,q\le 5\times 10^5$。

题解

考虑把贡献拆到每个点上,枚举点 $x$,找到其在链中出现的所有位置,则所有相邻两次出现 $p,q$ 之间的区间 $[p+1,q-1]$ 的子区间中均没有 $x$。若以 $l,r$ 为两维建立坐标系,则做矩形加即可处理此类贡献,最终答案用总点数 $n$ 乘上区间个数,再减去 $[L,R]$ 所对应的矩形和即为答案。

赛时不会矩形求和,因此想出了分讨修改和询问区间关系的做法。具体地,若贡献区间为 $[s,t]$,询问区间为 $[L,R]$,分讨一下四个端点的大小关系可以得到修改对询问的总贡献量。这里有两个式子分别是和 $L,R$ 有关的二次函数,因此需要开多个树状数组分别记录系数,还需要记录另外两种的贡献和和贡献次数,使用六棵树状数组维护扫描线即可,具体推导过程此处略去。

该做法复杂度高的原因是每个点会在多个链中出现,导致矩形加的次数太多。考虑减少矩形加的个数,先从链的情况入手,这时发现若从链头到链尾枚举点 $x$,则某条链中出现点 $x$ 的 $x$ 是一段区间,也即每条链只会出现一次,消失一次。因此总变化量为 $O(m)$,所以本质不同的 $[p+1,q-1]$ 只有 $O(m)$ 个,使用 set 动态维护目前空位的连续段,区间加带上消失和出现的时间戳之差,即贡献的次数作为系数即可。

考虑把上面的做法拓展到树上,直接将整棵树重链剖分,并把每条链拆成若干条重链,这样每条链在 DFS 序上就是连续的了。此时按照 DFS 序枚举点算贡献,再将所得的 $(s,t,x)$ 中的端点 $s,t$ 转化回原链序列的左右端点,同样扫描线计算答案即可。

时间复杂度 $O(m\log n\log (m\log n)+m\log n\log m+q\log q+q\log m)$,其中所有的 $m\log n$ 均为转化后的重链条数,分别是 set 维护连续段、修改、对查询排序和查询的复杂度。这里 set 常数大,修改查询有六棵树状数组,常数也大,但是树剖常数小,可以通过。空间复杂度 $O(m\log n\log m)$,记录扫描线时的修改,实际测试时空间甚至比时间更紧一些

D7T2 缠忆君影梦相见

题意

给定一张无重边自环的有向图,求有多少种删掉三条边的方案使得新图不强连通。多测,$T\le 10,n\le 50,m\le n(n-1)$。

题解

考虑把强连通转化为 $1$ 和其他每个点均互相可达,这个条件是充要的。因此可以通过原图和反图分别 BFS 一遍来判断是否强连通,用压位可以做到 $O(\frac{n^2}w)$,由于本题 $n\le 50$,可以用 long long 做到等价于 $O(n)$ 的复杂度。

通过上面的判断方式,我们注意到若原图和反图的 BFS 树没有改变,则连通性也不会改变。因此考虑直接跑出 BFS 树,只枚举这些边被删,枚举量就降到了 $O(n)$。具体实现时设 $f_{E,k}$ 表示在 $E$ 边集中删掉 $k$ 条边的方案数,若不连通则为 ${\left|E\right|}\choose k$,否则枚举跑出的 BFS 树边删去,递归到 $f_{E-e,k-1}$,写代码时打标记和统计方案数有一些细节,时间复杂度 $O(\frac {n^5}w)$,本题中即为 $O(n^4)$。

CF2101E 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-05-14 21:33:09

题意

给定一棵 $n$ 个点的树和长为 $n$ 的 $01$ 串 $s$。称长为 $m$ 的序列 $a$ 合法,当且仅当 $a$ 中元素两两不同,$\forall i\in[1,m],a_i\in[1,n],s_{a_i}=1$,且 $\forall i\in [1,m-2],2d(a_i,a_{i+1})\le d(a_{i+1},a_{i+2})$,其中 $d(u,v)$ 表示树上 $u,v$ 间简单路径的边数。对于 $x\in[1,n]$ 分别求 $a_1=x$ 时,合法 $a$ 序列的最大长度。$n\le 7\times 10^4$。

题解

首先看到 $2d(a_i,a_{i+1})\le d(a_{i+1},a_{i+2})$ 可以很自然地注意到每次路径长度至少翻倍,而路径长度不会超过 $n-1$,因此序列长度不会超过 $O(\log n)$,或许会对本题有帮助。

之后考虑使用 DP 解决该问题,注意到 DP 状态中只能记录开头或结尾元素,难以限制两两不同。然而仔细考虑 $d$ 的限制后发现,若两次经过点 $x$,设第二个 $x$ 的前缀为 $y$,则有 $d(y,x)>d(x,p_1)+d(p_1,p_2)+\cdots+d(p_k,y)$,然而 $d(y,x)$ 是 $y\to x$ 的唯一最短路径,不可能存在比其长度更短的,因此序列合法时不可能存在相同元素,故可以忽略该限制。

因此 DP 就是可行的了,最朴素的 DP 状态为 $f_{x,i,j}$ 表示 $a_1=x$,结尾为 $i$ 且最后一条路径长度为 $j$ 的最长合法序列长度。然而容易想到 $x$ 这一维并不必要,可以将最终的序列倒过来考虑,设 $f_{i,j}$ 表示开头为 $i$,第一条路径长度为 $j$ 的最长合法序列长度,通过在开头加元素来转移,状态数压缩到了 $O(n^2)$,但还是难以接受。

这时想起答案不会超过 $O(\log n)$,因此 DP 值不会超过 $O(\log n)$。考虑将 DP 值和状态互换,显然序列长度相同时第一条路径越长越优。因此设 $f_{i,x}$ 表示开头为 $i$,长为 $x$ 的合法序列中第一条路径的最大长度,这样状态数就变成 $O(n\log n)$ 的了。

之后考虑转移,$f_{j,x+1}\leftarrow d(i,j)$ 需要满足 $2d(i,j)\le f_{i,x}$。考虑枚举 $x$ 进行 $O(\log n)$ 轮转移,并使用点分治优化,即在分治中心处转移所有路径经过中心的 $(i,j)$ 对。则 $i$ 转移到 $j$ 的值为 $d_i+d_j$,有限制 $2(d_i+d_j)\le f_{i,x}$,拆开得到 $2d_i-f_{i,x}\le -2d_j$,这里 $d_x$ 为深度。因此以 $2d-f$ 为下标,用树状数组记录前缀 $d_i$ 的最大值即可进行转移。这里需要在点分治处理时正反跑两遍,总时间复杂度 $O(n\log^3 n)$,分别来自状态数、点分治和树状数组,在 6s 的时限下已经可以通过,代码

考虑能否优化到 $O(n\log^2n)$,发现正反跑两遍的过程实际上是把 $p_i\ne p_j$ 的限制拆成了 $p_i<p_j$ 或 $p_i>p_j$,其中 $p_x$ 表示 $x$ 所在的分治中心子树。如果能将所有子树的信息预处理到一起,查询时快速去掉 $p_j$ 子树内的信息并转移,就能降低复杂度。考虑把上式整个取反为 $f_{i,x}-2d_i\ge 2d_j$,在每个下标上维护 $d$ 的最大值和所属子树不同的次大值,并预处理出后缀信息。由于最大值和次大值信息合并可以 $O(1)$ 实现,整个后缀处理仍是线性的,查询时取子树外的最大值即可。

注意到查询的 $d_j$ 不会超过 $siz_u$,因此把超过 $2siz_u$ 的下标上的值均赋到 $2siz_u$ 上即可,数组大小得到保证,总时间复杂度 $O(n\log^2n)$,代码。由于常数较大,实际并没有比原来快多少。事实上限制式可以整体除以 $2$,得到 $\lfloor \frac{f_{i,x}-2d_i}2\rfloor\ge d_j$,可以通过数组大小少个 $2$ 的常数,然而也没快多少,可能是实现得比较粗糙了。

APIO2025 游记

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-05-19 09:42:21

用遗憾的时间

创造悲剧的世界

支离破碎的绚烂

都升上了月夜

Day.-16 - Day.-8(4.29-5.7)

在近两个月的文化课后去了 SD 二轮省集。

起到了一定的复健作用,但感觉整体打得不怎么样。

Day.-5 - Day.-1(5.10-5.14)

回 wfyz 了,补了一些省集模拟赛题解。

牢崔和曹神也来学校了,羽毛球打爽了。曹神还给了几道好题,感觉很牛。

Day.0(5.15)

早上从学校出发,火车上玩了三个小时的猜盐,你怎么知道我默写白鸟歌词一遍过了

到学校报道,送了一个 U 盘,但是后来测出速度很慢。

回宿舍,发现同校另外三个同学住三人间,我在隔壁。申请换宿未果,怄火,开睡了。

晚上试机,遇到了黑塔和蘑菇。给的 NOIP2024 题,我只写了 T3 输出 1 就摆了,后来发现黑塔极限写过了 T4,恐怖。

之后回宿舍跟同学玩 MC,最后仨人全死了,耻辱下播回去睡觉了。

Day.1(5.16)

白天上了四节课,只听懂了 eps 道题,之后有时间可能会再看看。另外知道了聆听是谦辞

晚上开幕式,AI 痕迹严重,并且有逆天演出和宣传片。最后杜子德主席讲话,感觉还是收敛了,最爆的可能是无意为之的打交道,这是视频。回宿舍写了写去年 T1 就睡了。

Day.2(5.17)

要比赛了,感觉挺紧张的,害怕跟 WC 一样寄掉,听了一早上的歌缓解。

进场,开题,T1 咋是交互,先写了 $25$。之后想到如果可以恰好表示出 $[1,n]$ 中所有的数,就可以二分答案了。但是这个感觉做不了,思考两个小时后放弃了,准备往后开题。

大概看了看后两题,很幸运地猜对了较简单的题是 T3,也很迅速地想到了两两配对垂直的结论。然而我不会实现,只推了一个分讨很多且复杂度未知的做法,感觉也不会做啊。于是在三个小时的时候我改变了看法,又认为 T3 比较困难了。

这时没啥进展的我已经慌了,加上 WC 没打好的心理压力,状态比较寄,去上了个厕所也没啥好转。回来看 T2 仍然感觉不可做,只写了前两个包。之后一直在三个题上随机游走,啥都没想出来。最后半个小时调 T2 的树也没调出,$25+12+16=53$ 遗憾离场了,稳打铁。

出场讨论发现 T3 最好做,天塌了,吃完饭回宿舍直接开睡。复评也没啥意外,但好像很多人因为交互库原因挂分了,和我也没啥关系。

晚上讲题,听到了很多有趣的故事,也被 T2 的超级分讨震撼到了。T1 我的思路跟正解不咋沾边,而 T3 我注意到了大部分,最后却没有想到好的实现,只能说还是菜了吧。

回宿舍,晚上颓到十二点,本来想睡却发现群里有 OI 你画我猜,进去又玩了一个小时,一点才睡。

生长向死亡

容不下一刻彷徨

Day.3(5.18)

上午还是去了剧场,结果很快掉线,再加上课件开头就提到可以放心摸鱼,于是第一次课间就回宿舍了。摆了一会,睡了一会。

下午去参观,群里传不让带手机于是没带,结果只是不让用而已,怄火。参观的东西也不多,然后看了几个纪录片就走了。回宿舍后一直在打集。

晚上闭幕式,AI 痕迹严重,并且有逆天演出和宣传片。另有草台班子使得人名对不上,证书发错之类,虽然和我没关系但确实逆天。同校一金两铜,同宿舍两银,感觉都很牛。回宿舍后在大厅看了几个老哥弹钢琴,最后收拾好东西就睡了。

总之我的第一次 APIO 就这样结束了。感觉 WC 是做好了打铁准备,却难以接受没有注意到猫粮性质的失误;这次是能接受自己水平和心态上的不足,却不太甘心再次空手而归。不管了,反正已经过去了,至少也积累了些经验,不如接着提升自己,之后再战吧。

莲瓣入水而不苦根茎,勿执着

【学习记录】点分治

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-05-19 21:00:58

点分治之前就学过,最近机房有人让我帮调,曹神又给了道好题,自己又学了点分树,因此简单记录一下。

点分治即每次找到树的重心,并以其为中心分治,处理跨过中心的所有路径,再递归下去处理不经过中心的子树内路径。处理方式为枚举其子树,先处理该子树与之前子树之间的路径,再将该子树内的点记录下来以便之后查询。由于以重心为根时,其子树大小不超过 $\frac{siz}2$,因此每个节点只会被处理 $O(\log n)$ 次,总复杂度为 $O(n\log n)$。思想大概就是这样,下面是例题。

P3806 【模板】点分治 1

题意

给定一棵树,边有边权,$q$ 次询问树上是否存在长为 $k$ 的路径,$n\le10^4,q\le 100,k\le 10^7$。

题解

板题,直接点分治,开桶记录目前是否存在与分治中心距离为 $i$ 的路径,每次处理时枚举所有询问更新答案。需要注意处理完某个中心后不能清空整个桶,而是只单点清空所用到的,具体做法为另开数组记录所有 $d$,最后遍历该数组清空,这也是大部分点分治题保证复杂度的关键。时间复杂度 $O(qn\log n)$,空间复杂度 $O(n+k)$。

25.02-MX-D1T2 毒药

题意

给定一棵树,点有点权 $a_i$,求满足 $a_x\oplus a_y\le \bigoplus_{i\in Path(x,y)} a_i$ 的二元组 $(x,y)$ 个数。$n\le2\times 10^5,a_i\le 2^{60}$。

题解

记 $d_i$ 表示 $i$ 到根的路径异或和,则原条件转化为 $a_x\oplus a_y\le d_x\oplus d_y\oplus a_{LCA(x,y)}$。则容易想到枚举 LCA,统计不同子树之间点对的贡献。然而这样遍历的总次数没有保证,所以使用点分治,上式中 $d$ 也变成点到分治中心的路径异或和。则问题转化为动态维护集合 $S$,集合元素为 $(a,d)$ 二元组,多次询问 $(a_x,d_x)$,查询集合 $S$ 中有多少 $(a_y,d_y)$ 满足 $a_x\oplus a_y\le d_x\oplus d_y\oplus a_{rt}$。

发现只有令含 $x$ 项和含 $y$ 项分别在同侧,才能快速维护数量。但小于号没有交换律,所以先变不等号并移项,得到 $a_x\oplus d_x\oplus a_{rt}\ne a_y\oplus d_y$,然后按照 $a\oplus d$ 分类后枚举 LCP 长度,分讨不同的最高位,最后加上相等的贡献即可。实现只需开一棵以 $a\oplus d$ 为键值的 Trie 树,每个点记录子树内 $a$ 该位为 $0$ 或 $1$ 的个数,可通过 $a\oplus d$ 的情况得到 $d$ 该位的值。查询时从根到对应叶子遍历一条链,处理链上节点的兄弟节点即可。

本题点分治部分仅有基本应用,在计算贡献时用到了 Trie 树,这里清空只需要清掉开的所有点即可。可能没有提交通道,只有计算贡献部分的原题 P7502,之后有时间可能会造造数据。

P10421 [蓝桥杯 2023 国 A] 树上的路径

题意

给定一棵树,求树上所有长度在 $[L,R]$ 内的路径长度和,长度为边数。$n\le 10^6$。

题解

先点分治,考虑如何统计路径长度和。若枚举到 $i$ 点,则之前子树内的 $j$ 需满足 $d_i+d_j\in [L,R]$,才能产生 $d_i+d_j$ 的贡献。即答案要加上 $\sumL-d_i\le d_j\le R-d_i$,拆括号后只需要求区间内个数和 $d_j$ 的和,开两棵树状数组即可实现,时间复杂度 $O(n\log^2 n)$。

但还是不够优秀,考虑求贡献时的实质,其实是钦定顺序为 $p_j<p_i$,在 $i$ 处计算贡献,其中 $p_x$ 表示 $x$ 所在的分治中心子树。因此若先把所有子树的贡献统计下来,并从中剔除当前子树的贡献来计算,就可以降低复杂度。对于本题,可以先将所有 $d_x$ 放入桶中并做前缀和,枚举某棵子树时对内部所有 $d_x$ 同样处理,求值时区间查询后用总值减去子树内的值即可。

问题在于对桶做前缀和需要值域大小的复杂度,然而某子树内的 $d$ 不会超过子树大小,因此总值需要的数组大小为 $siz_u$,单个子树内需要的数组大小为 $siz_v$,分析得总时间复杂度为 $O(n\log n)$,实际运行时由于有一定常数,且上个做法中的树状数组常数较小,优化后所用时间大概是上个做法的一半。优化后求贡献的代码片段:

struct nod
{
	ll s[N]; int len;
	void cle(int x)
	{
		len=x;
		for(int i=0;i<=len;i++) s[i]=0;
	}
	void add(int p,ll x) {s[p]+=x;}
	void build() {for(int i=1;i<=len;i++) s[i]+=s[i-1];}
	ll qry(int p) {return s[min(p,len)];}
}Ta[2],Tb[2];
void sol(int u)
{
	d[u]=0,Ta[0].cle(siz[u]),Tb[0].cle(siz[u]),Ta[0].add(0,2);
	for(int v:e[u]) if(!vis[v])
	{
		tt=0,dfs(v,u);
		for(int i=1;i<=tt;i++) Ta[0].add(td[i],1),Tb[0].add(td[i],td[i]);
	}
	Ta[0].build(),Tb[0].build();
	for(int v:e[u]) if(!vis[v])
	{
		tt=0,dfs(v,u),Ta[1].cle(siz[v]),Tb[1].cle(siz[v]);
		for(int i=1;i<=tt;i++) Ta[1].add(td[i],1),Tb[1].add(td[i],td[i]);
		Ta[1].build(),Tb[1].build();
		for(int i=1;i<=tt;i++)
		{
			int l=max(0,L-td[i]),r=R-td[i];
			if(l>r) continue;
			ll x=Ta[0].qry(r)-Ta[1].qry(r),y=Tb[0].qry(r)-Tb[1].qry(r);
			if(l) x-=(Ta[0].qry(l-1)-Ta[1].qry(l-1)),y-=(Tb[0].qry(l-1)-Tb[1].qry(l-1));
			res+=x*td[i]+y;
		}
	}
}

CF2101E Kia Bakes a Cake

题意

给定一棵 $n$ 个点的树和长为 $n$ 的 $01$ 串 $s$。称长为 $m$ 的序列 $a$ 合法,当且仅当 $a$ 中元素两两不同,$\forall i\in[1,m],a_i\in[1,n],s_{a_i}=1$,且 $\forall i\in [1,m-2],2d(a_i,a_{i+1})\le d(a_{i+1},a_{i+2})$,其中 $d(u,v)$ 表示树上 $u,v$ 间简单路径的边数。对于 $x\in[1,n]$ 分别求 $a_1=x$ 时,合法 $a$ 序列的最大长度。$n\le 7\times 10^4$。

题解

首先看到 $2d(a_i,a_{i+1})\le d(a_{i+1},a_{i+2})$ 可以很自然地注意到每次路径长度至少翻倍,而路径长度不会超过 $n-1$,因此序列长度不会超过 $O(\log n)$,或许会对本题有帮助。

之后考虑使用 DP 解决该问题,注意到 DP 状态中只能记录开头或结尾元素,难以限制两两不同。然而仔细考虑 $d$ 的限制后发现,若两次经过点 $x$,设第二个 $x$ 的前缀为 $y$,则有 $d(y,x)>d(x,p_1)+d(p_1,p_2)+\cdots+d(p_k,y)$,然而 $d(y,x)$ 是 $y\to x$ 的唯一最短路径,不可能存在比其长度更短的,因此序列合法时不可能存在相同元素,故可以忽略该限制。

因此 DP 就是可行的了,最朴素的 DP 状态为 $f_{x,i,j}$ 表示 $a_1=x$,结尾为 $i$ 且最后一条路径长度为 $j$ 的最长合法序列长度。然而容易想到 $x$ 这一维并不必要,可以将最终的序列倒过来考虑,设 $f_{i,j}$ 表示开头为 $i$,第一条路径长度为 $j$ 的最长合法序列长度,通过在开头加元素来转移,状态数压缩到了 $O(n^2)$,但还是难以接受。

这时想起答案不会超过 $O(\log n)$,因此 DP 值不会超过 $O(\log n)$。考虑将 DP 值和状态互换,显然序列长度相同时第一条路径越长越优。因此设 $f_{i,x}$ 表示开头为 $i$,长为 $x$ 的合法序列中第一条路径的最大长度,这样状态数就变成 $O(n\log n)$ 的了。

之后考虑转移,$f_{j,x+1}\leftarrow d(i,j)$ 需要满足 $2d(i,j)\le f_{i,x}$。考虑枚举 $x$ 进行 $O(\log n)$ 轮转移,并使用点分治优化,即在分治中心处转移所有路径经过中心的 $(i,j)$ 对。则 $i$ 转移到 $j$ 的值为 $d_i+d_j$,有限制 $2(d_i+d_j)\le f_{i,x}$,拆开得到 $2d_i-f_{i,x}\le -2d_j$,这里 $d_x$ 为深度。因此以 $2d-f$ 为下标,用树状数组记录前缀 $d_i$ 的最大值即可进行转移。这里需要在点分治处理时正反跑两遍,总时间复杂度 $O(n\log^3 n)$,分别来自状态数、点分治和树状数组,在 6s 的时限下已经可以通过,代码

考虑能否优化到 $O(n\log^2n)$,发现正反跑两遍的过程实际上是把 $p_i\ne p_j$ 的限制拆成了 $p_i<p_j$ 或 $p_i>p_j$,其中 $p_x$ 表示 $x$ 所在的分治中心子树。如果能将所有子树的信息预处理到一起,查询时快速去掉 $p_j$ 子树内的信息并转移,就能降低复杂度。考虑把上式整个取反为 $f_{i,x}-2d_i\ge 2d_j$,在每个下标上维护 $d$ 的最大值和所属子树不同的次大值,并预处理出后缀信息。由于最大值和次大值信息合并可以 $O(1)$ 实现,整个后缀处理仍是线性的,查询时取子树外的最大值即可。

注意到查询的 $d_j$ 不会超过 $siz_u$,因此把超过 $2siz_u$ 的下标上的值均赋到 $2siz_u$ 上即可,数组大小得到保证,总时间复杂度 $O(n\log^2n)$,代码。由于常数较大,实际并没有比原来快多少。事实上限制式可以整体除以 $2$,得到 $\lfloor \frac{f_{i,x}-2d_i}2\rfloor\ge d_j$,可以通过数组大小少个 $2$ 的常数,然而也没快多少,可能是实现得比较粗糙了。

P6329 【模板】点分树 | 震波

题意

给定一棵树,点有点权,需要支持单点修改,查询距某点不超过 $k$ 的点权和,两点间距离为边数。强制在线,$n,q\le 10^5$。

题解

点分树是点分治的应用之一。具体地,令分治中心以上一层分治中心为父亲,建出一棵点分树。与点分治类似,点分树的深度不会超过 $O(\log n)$,同时所有点的子树大小之和也是 $O(n\log n)$ 级别的。这允许我们暴力枚举某个点到根路径上的所有点,也允许对每个点记录其子树内所有点的信息。

这时对于某两点 $u,v$ 间的路径,我们用点分树上 LCA 代替原树 LCA 作为分界点,这样对于固定点 $u$,不同的分界点 $x$ 只有 $dep(u)$ 个,可以枚举。这时与 $u$ 以 $x$ 为 LCA 的节点为 $x$ 子树中除去 $s$ 子树的部分,$s$ 为 $x$ 子节点中子树内包含 $u$ 的。因此对每个点 $x$,分别记录其子树内与 $x$ 和 $x$ 父亲间路径的情况,查询时枚举 $x$,并用 $x$ 和 $s$ 的信息处理出有效信息即可。

对于本题,有 $dis_{u,v}=dis_{u,x}+dis_{x,v}$,枚举 $x$ 后要查询 $dis_{x,v}\le k-dis_{u,x}$ 的点权和。因此在每个点上要分别记录其子树内的点与其和其父亲距离为 $d$ 的点权和,求贡献时用 $x$ 对应区间的和减去 $s$ 对应区间的和即可。因此使用 $2n$ 棵动态开点线段树分别维护区间和,修改时对其到根路径上的所有点均修改即可。

时间复杂度为 $O((n+q)\log^2n)$,用 $O(1)$ LCA 求距离可去掉一个 log;空间复杂度理论上是 $O(n\log^2 n)$ 的,因为线段树的有效下标总数为 $O(n\log n)$,然而这里远远不满,用 vector 动态开点可以通过。主函数代码:

signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m,mx[0]=n,T[0].build(n),T[1].build(n);
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++)
	{
		int u,v; cin>>u>>v;
		e[u].pb(v),e[v].pb(u);
	}
	tot=n,getrt(1,0),ra=rt,getrt(rt,0),solv(rt),dfs0(1,0),dfs1(1,1),dfs2(ra);
	for(int u=1;u<=n;u++) for(int i=id[u];i<id[u]+s[u];i++)
	{
		int v=p[i]; T[0].update(u,0,n,dis(v,u),a[v]);
		if(f[u]) T[1].update(u,0,n,dis(v,f[u]),a[v]);
	}
	while(m--)
	{
		int op,x,y; cin>>op>>x>>y,x^=last,y^=last;
		if(op)
		{
			int u=x,dt=y-a[x]; a[x]=y;
			while(u)
			{
				if(f[u]) T[1].update(u,0,n,dis(x,f[u]),dt);
				T[0].update(u,0,n,dis(x,u),dt),u=f[u];
			}
		}
		else
		{
			int u=x,lp=x; last=0;
			while(u)
			{
				int d=y-dis(u,x);
				if(d>=0)
				{
					last+=T[0].query(u,0,n,0,d);
					if(u!=x) last-=T[1].query(lp,0,n,0,d);
				}
				lp=u,u=f[u];
			}
			cout<<last<<'\n';
		}
	}
	return 0;
}

CF925D 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-09-12 19:34:19

思路

把初始开启的走廊放到一张图上,发现这张图上的简单路径一定能走,所以这张图上 $1$ 到 $n$ 的最短路就是一种方案,设其长度为 $d$。

考虑通过别的方法走到 $n$,发现若 $(1,n)$ 初始关闭,那么走完第一条边后就会开启,只要再走回 $1$ 就可以直接走到 $n$。那么 $1\to x\to y\to 1\to n$ 就是一种路径,其中 $(1,x),(x,y)$ 初始开启,$(y,1),(1,n)$ 初始关闭,长度为 $4$。

由于这已经是最短的初始不完整的路径,所以若最短路长度 $d\le 4$,就可以直接用最短路。

分析这种路径什么时候存在,发现只要有点 $y$ 与点 $1$ 的距离为 $2$,那么就存在 $x$ 使得 $(1,x),(x,y)$ 初始开启,且 $(1,y)$ 初始关闭,否则距离会变为 $1$。同时如果 $(1,n)$ 初始开启,则最短路 $d=1$,不会再用长为 $4$ 的路径,所以 $(1,n)$ 初始关闭,可以确定有这样的路径。

如果没有这种路径,那么初始与 $1$ 号点在同一个连通块中的所有点全都与 $1$ 号点有直接连边,那么先走到 $x$,以 $x$ 为新的起点寻找上一种路径,也就是 $1\to x \to y\to z\to x\to n$,其中 $(z,x),(x,n)$ 初始关闭,其余初始开启,长度为 $5$。

考虑这种路径什么时候存在,发现同上只要有点 $z$ 与点 $x$ 的距离为 $2$ 即可。那么当且仅当所有与 $1$ 连通的点 $x$ 都没有合法的 $z$ 时会无解,也就是连通块内每个点都与其他所有点有连边,即这个连通块子图是完全图。

由于在完全图上每次走边都会使出发点被完全孤立开来,只会让完全图连通块越来越小,所以这种路径也不存在时就可以确定无解了。

具体寻找路径时直接遍历所有的出边即可,时间复杂度的话第一种路径时每条边只会作为 $(x,y)$ 被搜到一次,第二种路径时 $(x,y,z)$ 似乎相当于三元环记数那种复杂度?笔者太菜不会证。

代码

#include<iostream>
#include<vector> 
#include<queue>
using namespace std; 
const int N=3e5+10;
int n,m,fa[N],dis[N],te[N];
vector <int> edge[N]; 
queue <int> q;
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int u,v; cin>>u>>v;
		edge[u].push_back(v),edge[v].push_back(u);
	}
	q.push(n),dis[n]=1;
	while(q.size())
	{
		int u=q.front(); q.pop();
		for(int v:edge[u]) if(!dis[v]) dis[v]=dis[u]+1,fa[v]=u,q.push(v);
	} 
	if(dis[1]&&dis[1]<=5)
	{
		cout<<dis[1]-1<<'\n'; int p=1;
		while(p!=n) cout<<p<<' ',p=fa[p];
		cout<<n; return 0;
	} \/\/原图最短路 
	for(int x:edge[1]) te[x]=1;
	for(int x:edge[1]) for(int y:edge[x]) if(y!=1&&te[y]!=1) 
	{
		cout<<4<<'\n'<<1<<' '<<x<<' '<<y<<' '<<1<<' '<<n;
		return 0;
	} \/\/第一种 
	for(int x:edge[1])
	{
		for(int y:edge[x]) te[y]=x;
		for(int y:edge[x]) if(y!=1) for(int z:edge[y]) if(z!=x&&te[z]!=x)
		{
			cout<<5<<'\n'<<1<<' '<<x<<' '<<y<<' '<<z<<' '<<x<<' '<<n;
			return 0;
		}
	} \/\/第二种 
	cout<<-1;
	return 0;
}
共 137 篇博客