题目

题目链接:https://www.luogu.com.cn/problem/P2157

做法

$ f[i][j][k] $ 表示第 $ i $ 个数字,$ j $ 表示最后一个数字在 $ j+k $ 的位置,然后 $ k $ 用二进制表示后面的情况,对于 $ f[i][j][k] $,设 $ x $ 为包括他以内往后 $ b[i]+1 $ 位第一个没有做菜的位置,然后用 $ f[i][j][k] $ 贡献给 $ f[x] $ 即可。

当然,需要提前处理每个位置的哪些二进制合法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<cstdio>
#include<cstring>
#define N 1100
using namespace std;
inline int lowbit(int x){return x&-x;}
int f[N][10][260]/*表示这个位置自己优先搞到了*/;
int n,m,a[N],b[N];
bool he[N][260]/*表示这个学生哪个二进制是非常合法的*/;
inline int mymin(int x,int y){return x<y?x:y;}
inline int mymax(int x,int y){return x>y?x:y;}
void dfs(int x,int k,int limit/*表示限制后面几位*/,int id)//用来处理合法情况
{
he[id][k]=1;
if(x>limit)return;
dfs(x+1,k,mymin(limit,b[id+x]+x),id);
dfs(x+1,k^(1<<x),limit,id);
}
inline int find2(int x){return x==1?0:(x==2?1:(x==4?2:(x==8?3:(x==16?4:(x==32?5:(x==64?6:(x==128?7:8)))))));}
inline int cost(int x,int y){return (x|y)-(x&y);}
int ans=0;
inline void solve(int x,int k,int pre/*前面那个数组*/,int co)//贡献情况
{
if(x==n+1)
{
ans=mymin(ans,co);
return;
}
int shit=(1<<(b[x]+1))-1;
for(int ed=shit^k;ed;ed=ed^lowbit(ed))
{
int y=find2(lowbit(ed));
if(!he[x][k^(1<<y)])continue;
if(f[x][y][k^(1<<y)]==-1 || co+cost(a[x+y],pre)<f[x][y][k^(1<<y)])f[x][y][k^(1<<y)]=co+cost(a[x+y],pre);
}
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
memset(f,-1,sizeof(f));
memset(he,0,sizeof(he));
scanf("%d",&n);ans=999999999;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&b[i]);
b[i]=mymin(b[i],n-i);
}
for(int i=1;i<=n;i++)dfs(0,0,7,i);
for(int i=0;i<=b[1];i++)
{
if(he[1][1<<i])f[1][i][1<<i]=0;
}
for(int i=1;i<=n;i++)
{
int ed=(1<<(b[i]+1))-1;
for(int k=1;k<=ed;k++)
{
if(!he[i][k])continue;
int cnm=find2(lowbit(((1<<(b[i]+2))-1)^k));
for(int t=k;t;t=t^lowbit(t))
{
int j=find2(lowbit(t));
if(f[i][j][k]!=-1)solve(cnm+i,k>>cnm,a[i+j],f[i][j][k]);
}
}
}
printf("%d\n",ans);
}
return 0;
}