题解 P8108
前言
Cnoi 永远的神!
思路
首先我们考虑只有两列的情况,发现这就是求一个 LCS 的问题:可以一列消一块,或者两列底部相同的时候都消掉。
不难发现推广到
然后注意到 LCS 问题如果不是排列只能做到
我们突然发现数据好像是随机的啊,那么这有啥性质呢?
冷静分析一下这和排列也差不多啊,每种颜色只出现了
代码
// Problem: P8108 [Cnoi2021]绀珠传说
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P8108?contestId=46585
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
//And in that light,I find deliverance.
#include<bits/stdc++.h>
// #pragma GCC optimize("Ofast")
// #pragma GCC optimize("unroll-loops")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
using namespace std;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int tree[1003],n=read();
int a[1003][1003];
vector<int> v[1003][1003];
void add(int x,int k)
{
while(x<=n) tree[x]=max(tree[x],k),x+=x&(-x);
return ;
}
int find(int x)
{
int res=0;
while(x) res=max(res,tree[x]),x-=x&(-x);
return res;
}
signed main()
{
for(int j=n; j>=1; --j)
for(int i=1; i<=n; ++i)
v[i][a[i][j]=read()].push_back(j);
int ans=n*n;
for(int i=1; i<n; ++i)
{
for(int j=1; j<=n; ++j) tree[j]=0;
int q=0;
for(int j=1; j<=n; ++j)
{
vector<pair<int,int>> z;
for(int k:v[i][a[i+1][j]])
z.push_back(make_pair(k,find(k-1)));
for(auto k:z)
q=max(q,k.second+1),add(k.first,k.second+1);
}
ans-=q;
}
printf("%d\n",ans);
return 0;
}