2019-1-26-对联

Problem

给你n个数$a_{1…n}$,问你这n个数用加法组成的所有数中∈[1,p]的数的个数。

询问有m次。每次询问给定一个 $p_i$

$n<13,m<1e6,p_i<1e18,a_i<5e5$

solution

大家可以先去看一下这道题(本题的前一部分) 传送门

设Min为$a_i$中的最小值。

所以,我们对于任意数x,若x可以被表示,$x+a_i×k$均可以被表示。

所以我们要求出余数$x∈[0,Min)$被表示所需的最小值。

考虑最短路

对于$x∈[0,Min),连接x与x+a_i$,边权为$a_i$

然后就可以跑dijkstra。

对于每一个询问.

然后就可以愉快的拿到50分了。

打成这样竟然只给50,这部分分给的

好了,现在要考虑询问之间的转移。

第一个想到前缀和优化。

但是,怎么优化呢?

给p数组和dis数组从小到大排个序。

然后将p表示成$s×Min+t$的形式,$dis_i$表示成$d×Min+c$的形式。

这样的话

好啦,这样就可以用前缀和+树状数组优化了。

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
#include <bits/stdc++.h>

using namespace std;

#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define mp make_pair
#define fst first
#define snd second

template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }

inline int read(){
int res = 0, fl = 1;
char r = getchar();
for (; !isdigit(r); r = getchar()) if(r == '-') fl = -1;
for (; isdigit(r); r = getchar()) res = (res << 3) + (res << 1) + r - 48;
return res * fl;
}
typedef long long LL;
inline LL Lread(){
LL res = 0, fl = 1;
char r = getchar();
for (; !isdigit(r); r = getchar()) if(r == '-') fl = -1;
for (; isdigit(r); r = getchar()) res = (res << 3) + (res << 1) + r - 48;
return res * fl;
}
typedef pair<LL, int> pii;
const int Maxn = 5e5 + 10;
struct MAP{
LL to, v;
};
LL mod;
LL a[Maxn], Min;
LL dis[Maxn];
bool vis[Maxn];
pii ASK[Maxn << 1];
LL ans[Maxn << 1];
vector<MAP> g[Maxn];
namespace BIT{
LL tre[Maxn];
inline int lowbit(int x) { return x & -x;}
inline void Add(int pos){for ( ++pos; pos <= mod; pos += lowbit(pos)) ++tre[pos];}
inline LL Query(int pos){
LL sum = 0;
for ( ++pos; pos >= 1; pos -= lowbit(pos)) sum += tre[pos];
return sum;
}
}
void dijstra(int s){
priority_queue<pii> Q;
memset(dis, 0x3f, sizeof dis);
Q.push(mp(0,0));
dis[s] = 0;
while(!Q.empty()){
int now = Q.top().snd;
Q.pop();
if(vis[now]) continue;
vis[now] = 1;
for (int i = g[now].size() - 1; i >= 0; --i){
int nxt = g[now][i].to;
if(vis[nxt]) continue;
if(dis[nxt] > dis[now] + g[now][i].v){
dis[nxt] = dis[now] + g[now][i].v;
Q.push(mp(-dis[nxt],nxt));
}
}
}
}
int main()
{
freopen("couplet.in", "r", stdin);
freopen("couplet.out", "w", stdout);
LL n = read(), m = read();
Min = 1e9 + 10;
for (int i = 1; i <= n; ++i) {
a[i] = read();
chkmin(Min, a[i]);
}
mod = Min;
for (int i = 0; i < Min; ++i)
for (int j = 1; j <= n; ++j){
int nxt = (i + a[j]) % mod;
if(nxt == i) continue;
g[i].push_back((MAP){nxt, a[j]});
}
dijstra(0);
sort(dis + 1, dis + 1 + Min);
for (int i = 1; i <= m; ++i) ASK[i].fst = Lread(),ASK[i].snd = i;
sort(ASK + 1, ASK + 1 + m);
LL top = 0;
LL sum = 0;
BIT::Add(dis[0]);
for (int i = 1; i <= m; ++i){
LL p = ASK[i].fst;
while(top < (Min - 1) && dis[top + 1] <= p){
++top;
BIT :: Add(dis[top] % mod);
sum += 1ll * dis[top] / Min;
}
ans[ASK[i].snd] = 1ll * (p / Min) * (top + 1) - sum;
ans[ASK[i].snd] += 1ll * BIT :: Query(p % mod);
}
for (int i = 1; i <= m; ++i) printf("%lld\n",ans[i] - 1);
return 0;
}