题解:P11665 [JOI 2025 Final] 只不过是长的领带 2 / Just Long Neckties 2
cike_bilibili · · 题解
非正解,纯智慧。
但前半部分的 DP 是对的。
题目限制
但直接做还不太好,先找一下性质。
首先我们可以找到答案上界就是
由于长度的不同,我们可以理解为动态插入和调整一些人满足条件,我们可以在序列上从左往右考虑,我们考虑状态
每次转移无非就两种,调整和加入一个新领带,如果是调整,则一定是贪心的将离得最近的转移过来。
然而转移过来的其实是新状态起点的位置,我们还需要知道用当前状态从
先考虑
其实时间是能过的,但空间爆了。
我们考虑开个 std::vector<int>[V][V] 记录下标集合,每次二分一个位置,这样时间复杂度
容易想到压位,注意到 DP 转移时枚举了两个元素,可以看作枚举了一个元素,与整个集合的
时间复杂度很神奇,是
代码如下:
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
namespace Arisa{
inline int read(){
int ans=0,w=1;
char ch=getchar();
while(ch<48||ch>57){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch>=48&&ch<=57){
ans=(ans<<1)+(ans<<3)+ch-48;
ch=getchar();
}
return w*ans;
}
const int V=21;
int n;
int a[5000005];
int f[(1<<21)+5];
const int base=3,P=V/base+1;
vector<int>vec[P][25][(1<<base)+5];
void dp(){
for(int i=1;i<(1<<V);i++)f[i]=-1e9;
f[0]=0;
for(int i=0;i<(1<<V);i++){
int b=((1<<V)-1)^i;
int minn=n;
for(int j=0;j<V;j++){
if(i&(1<<j))continue;
for(int k=j/base;k<P;k++){
int now=(b>>(base*k))&((1<<base)-1);
if(!now)continue;
auto it=lower_bound(vec[k][j][now].begin(),vec[k][j][now].end(),f[i]+1);
if(it==vec[k][j][now].end())continue;
minn=min(minn,*it);
if(minn==f[i]+1)break;
}
if(minn==f[i]+1)break;
}
//其能到的位置就是 [f[i]+1,minn-1]
f[i]=minn-1;
int lst=-1;
for(int j=0;j<V;j++){
if(!(i&(1<<j))&&lst!=-1){//移位
f[i^(1<<lst)|(1<<j)]=max(f[i^(1<<lst)|(1<<j)],f[i]);
}
if(!(i&(1<<j))){//添位
f[i|(1<<j)]=max(f[i|(1<<j)],f[i]);
}
if(i&(1<<j))lst=j;
}
}
}
int main(){
n=read();
for(int i=1;i<=n;i++)a[i]=read()-1;
for(int i=1;i<n;i++){
int x=a[i],y=a[i+1];
if(x>y)swap(x,y);
int g=y/base,st=g*base;
for(int j=0;j<(1<<base);j++)if(j&(1<<y-st))vec[g][x][j].push_back(i);
}
dp();
int minn=V;
for(int i=0;i<(1<<V);i++)if(f[i]>=n-1){
int cnt=0;
for(int j=0;j<V;j++)if(i&(1<<j))++cnt;
minn=min(minn,cnt);
}
cout<<minn<<'\n';
return 0;
}
}
int main(){
return Arisa::main();
}
/*
1287 MB
1514 MB
10:44 subtask 123456
暂时没法做 n <= 5000000
11:29 卡常加神秘小优化可以拿下?
*/