2025_6_19 T3
Evan_Leo_Azir · · 题解
没想到吧,这是我不久前在NOIP模拟赛遇上的题目(但场上是
肉测是中位蓝的样子
首先,观察到
怎么折半?我们将前
对于A组
假设
对于B组
假设
对于
同样,如果
最后,处理出
时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int maxv=2e1+10,maxn=4e1+10;
int n,m,lim,ans;
int g[1<<23],fe[1<<22],pow_2[maxv],q1[maxn],q2[maxn];
bool f[1<<22];
int read()
{
int ret=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+(ch^48);ch=getchar();}
return ret*w;
}
void adde(int u,int v)
{
if(v<=lim) q1[u]|=pow_2[v-1];
else q2[u]|=pow_2[v-lim-1];
}//用01串表示点与 A,B 组的连边
void pre_all()
{
pow_2[0]=1;
for(int i=1;i<maxv;i++) pow_2[i]=pow_2[i-1]<<1;
}
void inpu() {n=read();m=read();}
void init()
{
ans=0;
memset(q1,0,sizeof(q1));memset(q2,0,sizeof(q2));
}
void pre()
{
lim=n>>1;
for(int i=1,u,v;i<=m;i++)
{
u=read(),v=read();
adde(u,v);adde(v,u);
}
for(int i=1;i<=n;i++)
{
if(i<=lim) q1[i]|=pow_2[i-1];
else q2[i]|=pow_2[i-lim-1];
}
}
void DP1()
{
for(int i=0;i<pow_2[lim];i++)
{
bool flag=0;fe[i]=pow_2[n-lim]-1;
for(int j=1;j<=lim;j++) if(i&pow_2[j-1])
{
fe[i]&=q2[j];//预处理出状态 i 最多能和哪些点拼接
if((i&q1[j])!=i) {flag=1;break;}
}
f[i]=!flag;
}
}
void DP2()
{
for(int i=0;i<pow_2[n-lim];i++)
{
int cnt=0;
bool flag=0;
for(int j=lim+1;j<=n;j++) if(i&pow_2[j-lim-1])
{
cnt++;
if((i&q2[j])!=i) {flag=1;break;}
}
if(!flag) {g[i]=cnt;continue;}
g[i]=0;
for(int j=lim+1;j<=n;j++) if(i&pow_2[j-lim-1]) g[i]=max(g[i],g[i^pow_2[j-lim-1]]);
}
}
void cal()
{
for(int i=0;i<pow_2[lim];i++) if(f[i])
{
int cnt=0;
for(int j=1;j<=lim;j++) if(i&pow_2[j-1]) cnt++;
ans=max(ans,g[fe[i]]+cnt);
}
}
void solve()
{
inpu();
if(n==1) printf("1\n");
else
{
init();
pre();
DP1();
DP2();
cal();
printf("%d\n",ans);
}
}
int main()
{
pre_all();
int t=1;
while(t--) solve();
return 0;
}
是实际上,这里用到了一个名为 子集状压 的算法(也称 sosDP),它同样是重要的进阶技巧,但此处只是皮毛,感兴趣的话可以自学