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。

对于每一个询问.

\[ans_i = \sum _{j = 1}^{dis_j \leq p_i} \frac{p_i - dis_j}{Min} \]

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

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

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

第一个想到前缀和优化。

但是,怎么优化呢?

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

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

这样的话

\[ans_i=\sum_{j = 1}^{dis_j \leq p_i} (a_i − b_j) − (c_i < d_j) \]

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

#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;
}



posted @ 2019-02-18 20:38  LZY_caiji  阅读(61)  评论(0编辑  收藏  举报
Live2D //博客园自带,可加可不加