专栏文章

题解:P8108 [Cnoi2021] 绀珠传说

P8108题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mioxj94r
此快照首次捕获于
2025/12/03 02:46
3 个月前
此快照最后确认于
2025/12/03 02:46
3 个月前
查看原文
考虑将连续相同的段删除等价于什么,即对所有相邻的两列选择一个公共子序列,对应位置合并一起删除。答案即为 n2n^2 减去所有相邻两列 LCS 长度之和。
注意到数据是随机生成的,即同一列中每种颜色的出现次数都是 O(1)\mathcal O(1) 的,直接枚举匹配的位置树状数组维护转移即可。
时间复杂度 O(n2logn)\mathcal O(n^2\log n)
参考代码:
CPP
#include<bits/stdc++.h>
#define ll long long
#define mxn 1003
#define pb push_back
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rept(i,a,b) for(int i=(a);i<(b);++i)
#define drep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
int n,ans,f[mxn],a[mxn][mxn];
vector<int>s[mxn];
inline void upd(int x,int y){
	for(;x<=n;x+=x&-x)f[x]=max(f[x],y);
}
inline int ask(int x){
	int s=0;
	for(;x;x-=x&-x)s=max(s,f[x]);
	return s;
}
signed main(){
	scanf("%d",&n),ans=n*n;
	rep(i,1,n)rep(j,1,n)scanf("%d",&a[j][i]);
	rep(i,2,n){
		rep(j,1,n)s[j].clear(),f[j]=0;
		drep(j,n,1)s[a[i-1][j]].pb(j);
		rep(j,1,n){
			for(int x:s[a[i][j]]){
				upd(x,ask(x-1)+1);
			}
		}
		ans-=ask(n);
	}
	cout<<ans;
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...