JZOJ 3512 游戏节目

虽然此题有 \(k \le 7\) 但是我愣是没看见,考场上写成了三维偏序然后重载运算符的时候很光荣地写炸了……

首先观察到 \(n \le 34\),并且有 \(2^{17}\)\(10^5\) 级别的,这个是数据结构题非常常见的数据范围啊!
出在我们的模拟赛里让我热血沸腾(要 素 察 觉

考虑折半搜索即 Meet in the Middle(都这个范围了不 MITM 大法还怎么活?
即,对于前 \(\dfrac n2\) 个我们做一次搜索,后 \(\dfrac n2\) 个我们也做一次搜索,然后合并答案。
既然 \(k \le 7\) 那么可以取补集之后按照选的节目数分类讨论变成二维偏序,不过写都写了不如硬核一点?

我们把每次搜索的结果都记为一个三元组 \((x,y,z)\),其中 \(x\) 表示 A 的总得分 - B 的总得分,\(y\) 表示 A 的总得分 - C 的总得分,\(z\) 表示选择的节目数。
那么我们的要求是最后 \(x,y > 0,z \ge k\)
也就是说,如果在第一次搜索里有一个三元组 \((x_1,y_1,z_1)\),第二次搜索里有另一个三元组 \((x_2,y_2,z_2)\),那么如果 \(x_1 + x_2,y_1 + y_2 > 0,z_1 + z_2 \ge k\),这两个结果就可以合并为一个合法方案。
看到不等式要学会移项,得 \(x_1 > -x_2,y_1 > -y_2,z_1 > k - z_2\),即对于第二次搜索里的每一个三元组,求出第一次搜索里有多少个三元组满足上述条件。
然后就是一个三维偏序了。
十分经典,不会的出门左转陌上花开。
树套树跑路,然鹅考场上排序的顺序写错了……

代码:

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <cstdio>
#include <algorithm>
#include <functional>
using namespace std;
const int N = 34;
const int M = 17;
const int CNT = 1 << 17;
int n,m,K,tot,a[N + 5],b[N + 5],c[N + 5];
long long ind[2 * CNT + 5];
int len;
struct note
{
long long x,y;
int cnt,w;
inline bool operator>(const note &o) const
{
return x > o.x || (x == o.x && y < o.y) || (x == o.x && y == o.y && cnt > o.cnt);
}
} w[2 * CNT + 5];
long long X,Y;
int cnt;
void dfs1(int k)
{
if(k > m)
{
w[++tot] = (note){X,Y,cnt,1},ind[tot] = Y;
return ;
}
dfs1(k + 1);
X += a[k] - b[k],Y += a[k] - c[k],++cnt;
dfs1(k + 1);
X -= a[k] - b[k],Y -= a[k] - c[k],--cnt;
}
void dfs2(int k)
{
if(k > n)
{
w[++tot] = (note){-X,-Y,max(K - cnt,0),2},ind[tot] = -Y;
return ;
}
dfs2(k + 1);
X += a[k] - b[k],Y += a[k] - c[k],++cnt;
dfs2(k + 1);
X -= a[k] - b[k],Y -= a[k] - c[k],--cnt;
}
struct segnode
{
int sum;
int ls,rs;
} seg[(CNT << 6) + 10];
struct node
{
int rt;
int ls,rs;
} tree[(CNT << 5) + 10];
int rt;
void insert(int x,int &p,int tl,int tr)
{
static int tot = 0;
if(!p)
p = ++tot;
++seg[p].sum;
if(tl == tr)
return ;
int mid = tl + tr >> 1;
x <= mid ? insert(x,seg[p].ls,tl,mid) : insert(x,seg[p].rs,mid + 1,tr);
}
int query(int l,int r,int p,int tl,int tr)
{
if(!p || (l <= tl && tr <= r))
return seg[p].sum;
int ret = 0;
int mid = tl + tr >> 1;
l <= mid && (ret += query(l,r,seg[p].ls,tl,mid));
r > mid && (ret += query(l,r,seg[p].rs,mid + 1,tr));
return ret;
}
void insert(int x,int y,int &p,int tl,int tr)
{
static int tot = 0;
if(!p)
p = ++tot;
insert(y,tree[p].rt,0,n);
if(tl == tr)
return ;
int mid = tl + tr >> 1;
x <= mid ? insert(x,y,tree[p].ls,tl,mid) : insert(x,y,tree[p].rs,mid + 1,tr);
}
int query(int l,int r,int x,int y,int p,int tl,int tr)
{
if(!p || (l <= tl && tr <= r))
return query(x,y,tree[p].rt,0,n);
int ret = 0;
int mid = tl + tr >> 1;
l <= mid && (ret += query(l,r,x,y,tree[p].ls,tl,mid));
r > mid && (ret += query(l,r,x,y,tree[p].rs,mid + 1,tr));
return ret;
}
long long ans;
int main()
{
freopen("show.in","r",stdin),freopen("show.out","w",stdout);
scanf("%d%d",&n,&K),m = n / 2;
for(register int i = 1;i <= n;++i)
scanf("%d",a + i);
for(register int i = 1;i <= n;++i)
scanf("%d",b + i);
for(register int i = 1;i <= n;++i)
scanf("%d",c + i);
dfs1(1),dfs2(m + 1),sort(w + 1,w + tot + 1,greater<note>()),sort(ind + 1,ind + tot + 1),len = unique(ind + 1,ind + tot + 1) - ind - 1;
for(register int i = 1;i <= tot;++i)
{
w[i].y = lower_bound(ind + 1,ind + len + 1,w[i].y) - ind;
if(w[i].w == 1)
insert(w[i].y,w[i].cnt,rt,1,len);
else
ans += query(w[i].y + 1,len,w[i].cnt,n,rt,1,len);
}
printf("%lld\n",ans);
}