南昌区域赛-G Eating Plan
链接:https://nanti.jisuanke.com/t/42582
Description
Bob is hungry now and he needs to eat some food. Alice puts n* dishes of food in front of him, numbered from $1 $ to $ n$. Alice tells him that he can only eat continuous dishes of food, or he will be poisoned by food. For example, if there are 1010 dishes, he can eat the food in the 2-nd, 3-rd and 4-th dishes but he can not eat the food in the 2-nd, 3-rd and 5-th dishes because there is the 4-th dish between them so it's not continuous. Furthermore, if he chooses to eat food in the i-th dish, he has to eat all food in that dish.
Bob's stomach has a strange feature that if there is at least t (=\(998857459\)) kg food in it, the weight in it will reduce t kg repeatedly until it is strictly lower than t kg. Also, if the weight of food in his stomach is exactly t kg, his stomach will be empty. Now Bob wants to eat the smallest number of dishes and remains no less than k kg food in his stomach. Can you tell him how many dishes he needs to choose?
Input
The first line contains two integers n and m(\(1\leq n\leq 100000,1\leq m\leq 10000\)), indicates the number of dishes and the number of queries.
The second line contains n integers \(a_1,a_2,\cdots,a_n\), which is a permutation of \(1, 2, \cdots, n\), indicates that there is \((a_i!)\) kg food in the i-th dish, where \(s! = 1 \times 2 \times 3 \times \cdots \times s\).
The third line contains mm integers, the i-th integer \(k_i(1\leq k_i<t)\) indicates a query with the lower bound of weight.
Output
Each line of mm lines contains an integer indicates the number of dishes Bob needs to choose in that query. If there is no way to reach Bob's target, output -1
instead.
Sample Input
4 2
1 2 3 4
29 31
Sample out
2
3
题意
给定n个数,m次询问
对于n个数,每个数的值是其阶乘值%998857459
每次询问一个数x,问最短的连续区间的和(取模意义下)大于x,输出最短的区间长度
题解
这个题不难,但是细节比较多
首先我们发现这个取模的数十分奇怪,我们对它质因数分解一下,果然,这不是个质数,它等于\(461 \times 773 \times 2803\),也就是说,超过2803的数,求阶乘再取模会等于0,且区间至多有2803个不同的数,我们先预处理一下区间,把所有等于0的数合并到不等于0的数上,让其区间长度+1,这样我们处理完之后的数组至多只有2803的大小,我们直接n^2暴力把所有区间和算出来,求出区间长度,对于询问二分答案即可
这里要注意,由于我们把0合并到了它右边第一个不等于零的位置上,所以我们其实在左端点是多算了一次的,比如下面的区间\(0,0,1,0,2\),如果求大于等于3的区间,我们将会输出5,因为1和2所代表的区间长度分别为3和2,所以我们要减去左端的0的个数.
此题只给了1s,如果\(2803*2803*O(map)\)时间复杂度不够,所以我们要先全部存起来再sort取最小值,求出对应值的区间最短值,由于值越大,区间长度不一定越大,我们还需要倒序取一遍min,之后直接二分,就可以通过此题了
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 50;
int a[N];
const ll mod = 998857459;
ll fac[N];
vector<ll> b;
vector<ll> num;
ll pre1[N];
ll pre2[N];
map<ll, ll> mp;
struct node {
ll val, len;
node (ll val = 0, ll len = 0): val(val), len(len) {}
bool operator < (const node &b) const {
if (val == b.val) {
return len < b.len;
}
return val < b.val;
}
}tmp[2900 * 2900];
ll val[N];
ll len[N];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
fac[0] = 1;
for (int i = 1; i < 2803; i++) {
fac[i] = fac[i - 1] * i % mod;
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (a[i] >= 2803) {
cnt++;
}
else {
num.push_back(cnt + 1);
b.push_back(fac[a[i]]);
cnt = 0;
}
}
for (int i = 0; i < b.size(); i++) {
pre1[i + 1] = (pre1[i] + b[i]) % mod;
pre2[i + 1] = (pre2[i] + num[i]);
}
int p = 0;
for (int i = 0; i < b.size(); i++) {
for (int j = i; j < b.size(); j++) {
ll now = (pre1[j + 1] - pre1[i] + mod) % mod;
ll l = pre2[j + 1] - pre2[i] - (num[i] - 1);
tmp[++p] = node(now, l);
}
}
sort(tmp + 1, tmp + p + 1);
cnt = 0;
for (int i = 1; i <= p; i++) {
if (tmp[i].val != tmp[i - 1].val || i == 1) {
val[++cnt] = tmp[i].val;
len[cnt] = tmp[i].len;
}
}
for (int i = cnt - 1; i >= 1; i--) len[i] = min(len[i + 1], len[i]);
while (m--) {
int x;
scanf("%d", &x);
int l = 1, r = cnt;
ll ans = 1e9;
while (l <= r) {
int mid = (l + r) >> 1;
if (val[mid] >= x) {
ans = min(ans, len[mid]);
r = mid - 1;
}
else l = mid + 1;
}
if (ans == 1e9) ans = -1;
printf("%lld\n", ans);
}
return 0;
}