题目:BZOJ3003.
题目大意:给定一个长度为
n
n
n序列
a
i
a_i
ai与给定
m
m
m种操作,
a
i
a_i
ai初始全为
0
0
0,每种操作给定一个长度,表示可以对这个长度的区间取反.现在要你用一定次数的操作把整个序列变成只有
k
k
k个位置为
1
1
1,其余全为
0
0
0,求最少操作数.
数据组数
T
≤
10
T\leq 10
T≤10,
1
≤
n
≤
1
0
4
,
1
≤
m
≤
100
,
1
≤
k
≤
10
1\leq n\leq 10^4,1\leq m\leq 100,1\leq k\leq 10
1≤n≤104,1≤m≤100,1≤k≤10.
对于这种区间染色的题,我们发现区间染色的操作非常麻烦,所以考虑把操作和序列都进行异或差分,现在操作变成相隔一定长度的两个位置取反,同时最终的序列变成了最多 2 k 2k 2k个位置为 1 1 1.
之后考虑把问题变为从终态到初态的最少步数,然后状压DP.设
f
[
S
]
f[S]
f[S]表示集合
S
S
S中的位置为
1
1
1其它位置为
0
0
0需要的最少步数,那么可以列出转移:
f
[
S
]
=
min
i
,
j
∉
S
{
f
[
S
∪
{
i
,
j
}
]
+
g
(
i
,
j
)
}
f[S]=\min_{i,j\notin S}\{f[S\cup\{i,j\}]+g(i,j)\}
f[S]=i,j∈/Smin{f[S∪{i,j}]+g(i,j)}
其中 g ( i , j ) g(i,j) g(i,j)表示把位置 i i i和位置 j j j同时消掉需要的最少步数,但是这个东西并不好求.
考虑若当前用长度为 l e n 1 len_1 len1的操作消掉了位置 i i i,则位置 i + l e n 1 i+len_1 i+len1会变为 1 1 1,之后用长度为 l e n 2 len_2 len2的操作消掉位置 i + l e n 1 i+len_1 i+len1,则位置 i + l e n 1 i+len_1 i+len1会变为 0 0 0且 i + l e n 1 + l e n 2 i+len_1+len_2 i+len1+len2变为 1 1 1…
我们会发现上述过程类似于一个最短路,对于每一个位置 i i i和每一个区间长度 j j j,则将位置 i i i和 i + j , i − j i+j,i-j i+j,i−j两个位置连接起来,跑个最短路就完事了.
一个小优化,容易发现每次转移的时候位置 ( i , j ) (i,j) (i,j)中有一个位置是可以钦定而不枚举的,这样就可以少 O ( k ) O(k) O(k)的时间复杂度了.
时间复杂度 O ( T ( n m + 2 2 k k ) ) O(T(nm+2^{2k}k)) O(T(nm+22kk)).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=10000,M=100,K=10,INF=(1<<30)-1;
int num[(1<<K*2)+9];
void Get_num(){
for (int i=0;i<=K*2;++i) num[1<<i]=i;
}
int n,m,sk,a[N+9],len[M+9];
int p[K*2+9],cp;
void Get_p(){
cp=0;
for (int i=1;i<=n;++i)
if (a[i]) p[++cp]=i;
}
struct side{
int y,next;
}e[N*M*2+9];
int lin[N+9],cs;
void Ins(int x,int y){e[++cs].y=y;e[cs].next=lin[x];lin[x]=cs;}
void Ins2(int x,int y){Ins(x,y);Ins(y,x);}
void Get_graph(){
for (int i=1;i<=n;++i) lin[i]=0;
cs=0;
for (int i=1;i<=m;++i)
for (int j=1;j<=n;++j){
if (j-len[i]>=1) Ins(j,j-len[i]);
if (j+len[i]<=n) Ins(j,j+len[i]);
}
}
queue<int>q;
int dis[K*2+9][N+9],vis[N+9];
void Bfs_dis(int id,int st){
for (int i=1;i<=n;++i) dis[id][i]=INF,vis[i]=0;
dis[id][st]=0;vis[st]=1;q.push(st);
for (;!q.empty();){
int t=q.front();q.pop();
for (int i=lin[t];i;i=e[i].next)
if (!vis[e[i].y]){
dis[id][e[i].y]=dis[id][t]+1;
vis[e[i].y]=1;
q.push(e[i].y);
}
}
dis[id][st]=INF;
}
int dp[(1<<K*2)+9];
void Get_dp(){
for (int g=0;g<1<<cp;++g) dp[g]=INF;
dp[(1<<cp)-1]=0;
for (int g=(1<<cp)-1;g>0;--g){
if (dp[g]==INF) continue;
int t0=g&-g;
if (g-t0==0) continue;
for (int i=g-t0;i;i-=i&-i){
int t1=i&-i;
dp[g^t0^t1]=min(dp[g^t0^t1],dp[g]+dis[num[t0]+1][p[num[t1]+1]]);
}
}
}
Abigail start(){
Get_num();
}
Abigail into(){
scanf("%d%d%d",&n,&sk,&m);++n;
for (int i=1;i<=n;++i) a[i]=vis[i]=0;
for (int i=1;i<=sk;++i){
int x;
scanf("%d",&x);
if (vis[x]) continue;
vis[x]=1;
a[x]^=1,a[x+1]^=1;
}
for (int i=1;i<=m;++i)
scanf("%d",&len[i]);
}
Abigail work(){
Get_p();
Get_graph();
for (int i=1;i<=cp;++i) Bfs_dis(i,p[i]);
Get_dp();
}
Abigail outo(){
printf("%d\n",dp[0]==INF?-1:dp[0]);
}
int main(){
int T;
start();
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}