2024.8.8

\(0+90+0+10\)

挂分:\(\text{T1}\) 数组开小了,\(\text{T2}\) 最后一个点不小心 \(\text{RE}\) 了,\(\text{T3}\) 炸了,\(\text{T4}\) 大样例没骗到

然后这四个题一眼题名以为出题人语文功底多好,却是四个 \(\text{galgame}\)

考场一眼 \(\text{T2}\) 莫名熟悉,还以为出原题 原来是前不久才刷过、、

T1 九次九日九重色

给定两个长度为 \(n\)\(1\sim n\) 的排列 \(P\)\(Q\),现在需要在 \(P\)\(Q\) 中分别取出长度为 \(k\) 两个子序列 \(A\)\(B\),满足 \(∀i∈[1,k]\)\(A_i \mid B_i\)。最大化 \(k\)

考场思路

想想好像不咋好搞,要在 \(B\) 里找 \(A\) 的倍数,还得保证最大化,肯定得 \(\text{DP}\),但是好像只会 \(O(n^2)\) 的,先写吧。然后就设 \(f_{i,j}\) 表示 \(B\) 中选到 \(i\) 且匹配了 \(A\) 里的前 \(j\) 个且 \(B_i\)\(A_j\) 匹配的最大匹配数。想转移,肯定至少要从 \(i-1\) 转移过来,好像不止,因为 \(i\) 可以不匹配。那么就得从 \(1\sim i-1\) 转移。\(j\) 也得从 \(1 \sim j-1\) 转移过来,这样才能对,那我天然就得到了一个 \(O(n^4)\)\(\text{DP}\)

\[\left\{\begin{matrix} ans =& \displaystyle\max_{i=1}^{n}\max_{j=1}^{n}f_{i,j} \\ f_{i,j} =& \displaystyle\max_{k=1}^{i-1}\max_{l=1}^{j-1}f_{k,l} + 1 \end{matrix}\right. \]

这一坨东西能有分都是神迹。

预处理贡献优化。我们再设一个 \(g_{i,j}\) 表示 \(\displaystyle\max_{k=1}^{i-1}\max_{l=1}^{j-1}f_{k,l}\)。既然这个东西是由 \(f_{i,j}\) 转出来了,就可以从 \(f_{i,j}\) 向后递贡献。先给离它最近的 \(f_{i+1,j+1}\) 刷上贡献: \(g_{i+1,j+1} = f_{i,j}\)。再考虑给下一阶段的 \(i\) 刷上递推过来的贡献,\(g_{i+1,j} = \max{g_{i+1,j},g_{i,j}}\)\(j\) 也是同理:\(g_{i+1,j} = \max{g_{i+1,j},g_{i+1,j-1}}\)

接下来你就把它优化到了 \(O(n^2)\)

但是空间只有 \(256 \text{MB}\),开俩 \(5000 \times 5000\) ,死了。又考虑到 \(5000 \times 5000\) 的范围的答案连 \(65535\) 都不到,于是就可以改 \(\text{short}\),于是就拿到了 \(30\)

Sol1

\(\text{std}\) 的思路。考虑到每一个数的倍数用倍增是 \(\log\) 级别的,所以可以预处理每个数的倍数,即整除关系配对。把配好对的二元组按位置关系排序。注意排序的时候要将 \(b\) 的元素位置逆序排,更优秀。在里面跑一个 \(\text{LIS}\),然后用 \(O(n\log n)\) 乱搞处理 \(\text{LIS}\),总复杂度就是 \(O(n \log^2 n)\)

顺便复习一下 \(O(n \log n)\)\(\text{LIS}\)。还是顺推贡献,\(f_i\) 会对所有 \(a_j > a_i,j>i\) 的点产生贡献,先离散化,把值域缩到 \(n\) 以内,再用线段树在值域 \([a_i+1,n]\) 刷上贡献就行了,线段树维护区间修改 \(\max\),单点查询 \(\max\)

const int N = 1e5+5;
int a[N];int c[N],b[N];int f[N];
struct TREE
{
	#define lson(x) x<<1
	#define rson(x) x<<1|1
	struct NODE
	{
		int l,r,val,lz;
	}tree[N<<2];
	void pushup(int x){tree[x].val = max(tree[lson(x)].val ,tree[rson(x)].val);}
	void build(int l,int r,int x)
	{
		tree[x].l = l,tree[x].r = r;
		if(l == r){tree[x].val = 0;return;}
		int mid = (l +r)>>1;
		build(l,mid,lson(x)),build(mid+1,r,rson(x));
		pushup(x);
	}
	void pushdown(int x)
	{
		if(tree[x].lz)
		{
			tree[lson(x)].lz = max(tree[x].lz,tree[lson(x)].lz);tree[rson(x)].lz =max(tree[x].lz,tree[rson(x)].lz);
			int mid =(tree[x].l + tree[x].r)>>1;
			tree[lson(x)].val = max(tree[lson(x)].val,tree[x].lz * (mid-tree[x].l+1));
			tree[rson(x)].val = max(tree[rson(x)].val,tree[x].lz * (tree[x].r-mid));
			tree[x].lz = 0;
		}
	}
	void update(int l,int r,int k,int x)
	{
		if(l<=tree[x].l&&tree[x].r<=r) {tree[x].lz = max(tree[x].lz,k);tree[x].val =max(tree[x].val,k*(tree[x].r-tree[x].l+1));return;}
		pushdown(x);
		int mid = (tree[x].l + tree[x].r)>>1;
		if(l<=mid) update(l,r,k,lson(x));
		if(r>mid) update(l,r,k,rson(x));
		pushup(x);
	}
	int query(int l,int r,int x)
	{
		if(l<=tree[x].l && tree[x].r <= r) return tree[x].val;
		pushdown(x);
		int mid = (tree[x].l + tree[x].r)>>1,res{-LONG_LONG_MAX};
		if(l<=mid) res =max(res, query(l,r,lson(x)));
		if(r>mid) res = max(query(l,r,rson(x)),res);
		return res;
	}
}tree;
signed main() 
{
	#ifdef LOCAL
	freopen("in.in","r",stdin);
	#endif
	int n = read();
	for(int i{1};i<=n;i++) c[i] = read();
	memcpy(b,c,sizeof(int)*(n+1));
	sort(b+1,b+1+n);
	int l = unique(b+1,b+1+n)-b-1;
	for(int i{1};i<=n;i++) a[i] = lower_bound(b+1,b+1+l,c[i])-b;
	f[1] = 1;
	tree.build(1,n,1);
	for(int i{1};i<=n;i++)
	{
		f[i] = tree.query(a[i],a[i],1)+1;
		tree.update(a[i]+1,n,f[i],1);
	}
	int ans{};
	for(int i{1};i<=n;i++) ans = max(ans,f[i]);
	writeln(ans);
}

Sol2

\(\text{XiaoLeMC}\) 的思路。其实大体是相同的,但是实现更简便。倒序跑 \(A\),在 \(B\) 内找到所有 \(A\) 的倍数,在线段树上维护序列 \(c\),修改 \(c_i\)\(c_{i+1} \sim c_n\) 的最大值 \(+1\),跑完输出 \(c\) 中最大值就行了, \(O(n \log^2 n)\)

T2 天色天歌天籁音

这题没啥说的,莫队维护区间众数板子。呃呃但是由于删除操作中的一些细节,导致有可能下标变 \(-1\),然后调了半天发现了,把完全死透改成了半死不活,就成 了 \(90\) ,以下是考场代码,把第三行和第二行换一下直接过。

while(rr<q[i].r) add(++rr);
while(rr>q[i].r) sub(rr--);
while(ll>q[i].l) add(--ll);
while(ll<q[i].l) sub(ll++);

T3 春色春恋春熙风

\(1\) 节点为根,每条边上都有一个 av 中的字符。一条简单路径被称为 \(\lceil\)\(\rfloor\),当且仅当路径上的字符重新排序后能成为回文串。求每个点子树中最长 \(\lceil\)\(\rfloor\) 的长度。

本来题就看错了,题上说的是这个串里所有的字符都得在回文串里。那我写的链的情况就完全错掉了。链的部分分应该是二分长度,然后 \(O(n)\)\(\text{check}\),做前缀和,\(O(n\log n)\) 出。呃呃,没有链的大样例,\(10\) 分都不太好拿。

首先比较显然的是,一个串能够排成回文串当且仅当,它的所有字符数量中奇数的数量不超过 \(1\)。对于字符只有 \(22\) 种(你猜为什么是 \(22\) 种,不就是给状压用的…),所以可以状压,用 \(0\) 表示偶,\(1\) 表示奇。

对于路径 \((u,v)\) ,状态可以差分求出。设 \(val_u\) 表示 \(u\)\(1\) 的路径上的字符状态,于是有 :\((u,v) = (val_u \oplus val_{lca}) \oplus (val_v \oplus val_{lca})\)(这个真的好聪明)。考虑到异或的特殊性,这就是 \(val_u \oplus val_v\)。于是一条路径合法当且仅当:\(\text{count}_{val_u \oplus val_v} \le 1\),就容易了。

  • 我们可以枚枚举点对,以 \(O(23)\) 的复杂度 \(\text{check}\) \(val_u \oplus val_v\),一个点对 \((u,v)\) 对答案的贡献是 \(d_u + d_v - 2 d_{lca}\),从 \(\text{lca}\) 向根贡献答案复杂度 \(O(23n^2)\),期望得分 \(30\)实测,如果你用线段树做到 \(O(n^2 \log n)\) 可以拿到 \(20\) 分)。

  • 优化复杂度,我们对 \(2^{22}\) 种状态开桶,从一个点 \(x\) 开始搜索,遍历子树,对于一个状态 \(val_u\) 进行 \(\oplus\),于是检查桶内有没有合法解。


int head[N];
struct EDGE
{
	int nxt,to;
	char c;
}e[N<<1];
int ecnt{};
void add(int u,int v,char c)
{
	e[++ecnt].nxt = head[u];e[ecnt].to = v;
	e[ecnt].c= c;head[u] = ecnt;
}
int f[N],d[N],du[N],siz[N],son[N];
char cfa[N];
int val[N];
int dfn[N];
int ori[N];
int tot{};
void dfs(int u,int fa)
{
	f[u] = fa;d[u] = d[fa]+1;
	dfn[u]= ++tot;
	ori[dfn[u]] = u;
	siz[u] = 1;
	for(int i{head[u]};i;i=e[i].nxt)
	{
		int v = e[i].to;
		if(v == fa) {cfa[u] = e[i].c;continue;}
		val[v]= val[u] ^ (1<<(e[i].c-'a'));
		dfs(v,u);
		siz[u] += siz[v];
		if(siz[v] > siz[son[u]]) son[u] = v;
	}
}
int ans[N];
int maxdep[N*10];
void dfs4(int u,int kp)
{
	for(int i{head[u]};i;i=e[i].nxt)
	{
		int v = e[i].to;
		if(v == son[u] || v == f[u]) continue;
		dfs4(v,0);
		ans[u] = max(ans[u],ans[v]);
	}
	if(son[u]) dfs4(son[u],1),ans[u] = max(ans[u],ans[son[u]]);
	if (maxdep[val[u]]) ans[u] = max(ans[u], maxdep[val[u]] - d[u]);  
	for (int i{}; i <= 21; i++) {  
		if (maxdep[val[u] ^ (1 << i)]) ans[u] = max(ans[u], maxdep[val[u] ^ (1 << i)] - d[u]);
	}
	maxdep[val[u]] = max(d[u], maxdep[val[u]]);
	for (int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if (v == son[u]) continue;
		for (int j = dfn[v]; j <= dfn[v]+siz[v]-1; j++) {
			int x = ori[j];
			if (maxdep[val[x]]) ans[u] = max(ans[u], maxdep[val[x]] + d[x] - 2 * d[u]);
			for (int k = 0; k <= 21; k++) {
				if (maxdep[val[x] ^ (1 << k)]) ans[u] = max(ans[u], maxdep[val[x] ^ (1 << k)] + d[x] - 2 * d[u]);
			}
		}
		for (int j = dfn[v]; j <= dfn[v]+siz[v]-1; j++) {
			maxdep[val[ori[j]]] = max(maxdep[val[ori[j]]], d[ori[j]]);
		}
	}
	if (!kp) 
		for (int i =dfn[u]; i <= dfn[u]+siz[u]-1; i++) maxdep[val[ori[i]]] = 0;
	
	
}
signed main()
{
	#ifdef LOCAL
	freopen("in.in","r",stdin);
	#endif
	cin.tie(0);
	char c;
	int n = read();
	for(int i{2};i<=n;i++) 
	{
		int u =read();char c;
		cin>>c;
		add(u,i,c);
	}
	dfs(1,0);
	dfs4(1,1);
	for(int i{1};i<=n;i++) writek(ans[i]);
	return 0;
 }
posted @ 2024-09-14 21:17  WanGMiNgWeI  阅读(4)  评论(0编辑  收藏  举报