分析:
很明显的线段树维护区间最小值,之后二分查找。
大的方向没问题,主要是很多小的地方妹处理不好.
1.取模找,类似于形成一个环,这种问题的处理方法其实以前也用过,但是当时忘了,办法是把区间长度搞成两倍即可,明显可以看到代码中线段树开的是1e6 * 4 * 2的
2.二分那部分当时也好混乱,但其实很简单
3.二分结束后,l就是答案,这时需要判断l与k的关系,因为为了方便环我们搞成了2 * k,然后就是更新的时候l与l + k都需要更新。
AC代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll inf = 0x3f3f3f3f3f3f; ll ar[100050], br[100050]; struct node { ll l, r; ll x; } tree[800050]; ll n, k; int cnt[100050]; bool flag; void pushup(ll p) { tree[p].x = min(tree[p<<1].x, tree[p<<1|1].x); } void build(ll p, ll l, ll r) { tree[p].l = l; tree[p].r = r; if(l == r) { if(l <= k) tree[p].x = ar[l] + br[l]; else tree[p].x = ar[l - k] + br[l - k]; return ; } ll mid = (l + r) >> 1; build(p<<1, l, mid); build(p<<1|1, mid + 1, r); pushup(p); } void updata(ll p, ll id, ll x) { if(tree[p].l == tree[p].r && tree[p].l == id) { tree[p].x = x; return ; } ll mid = (tree[p].l + tree[p].r) >> 1; if(mid >= id) updata(p<<1, id, x); else updata(p<<1|1, id, x); pushup(p); } ll query(ll p, ll l, ll r) { if(l <= tree[p].l && tree[p].r <= r) { return tree[p].x; } ll mid = (tree[p].l + tree[p].r) >> 1; if(r <= mid) return query(p<<1, l, r); if(l > mid) return query(p<<1|1, l, r); return min(query(p<<1, l, r), query(p<<1|1, l, r)); } int main() { //cout << inf << '\n'; scanf("%lld%lld", &k, &n); for(int i = 1; i <= n; ++i) scanf("%lld%lld", &ar[i], &br[i]); build(1, 1, 2 * k); for(int i = 1; i <= k; ++i) ++cnt[i]; for(int i = k + 1; i <= n; ++i) { flag = false; ll l = i % k; if(l == 0) l = k; ll r = l + k - 1; if(query(1, l, r) > ar[i]) continue; while(l < r) { ll mid = (l + r) >> 1; if(query(1, l, mid) <= ar[i]) r = mid; else l = mid + 1; } if(l > k) l -= k; ++cnt[l]; updata(1, l, ar[i] + br[i]); updata(1, l + k, ar[i] + br[i]); } int mx = 0, ans; for(int i = 1; i <= k; ++i) { mx = max(mx, cnt[i]); } flag = false; for(int i = 1; i <= k; ++i) { if(mx == cnt[i]) { if(flag) { printf(" %d", i - 1); } else { printf("%d", i - 1); flag = true; } } } //printf("%d", ans - 1); return 0; }
另外,学习了一下dalao的代码,代码量很少,毕竟因为线段树维护的东西比较简单,没有直接定义结构体,也没有写build(1,1,n)这个函数。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll maxn = 1e6 + 5; ll t[maxn<<3], cnt[maxn]; ll n, k; ll a, b; ll query(ll p, ll l, ll r, ll x, ll y) { if(x <= l && r <= y) return t[p]; ll mid = (l + r) >> 1; if(y <= mid) return query(p<<1, l, mid, x, y); if(x > mid) return query(p<<1|1, mid + 1, r, x, y); return min(query(p<<1, l, mid, x, y), query(p<<1|1, mid + 1, r, x, y)); } void updata(ll p, ll l, ll r, ll id, ll x) { if(l == r) return void(t[p] = x); ll mid = (l + r) >> 1; if(id <= mid) updata(p<<1, l, mid, id, x); if(id > mid) updata(p<<1|1, mid + 1, r, id, x); t[p] = min(t[p<<1], t[p<<1|1]); } int main() { scanf("%lld%lld", &k, &n); ll mx = 0; for(int i = 1; i <= n; ++i) { scanf("%lld%lld", &a, &b); ll l = i % k; if(l == 0) l = k; ll r = l + k - 1; if(query(1, 1, 2 * k, l, r) > a) continue; while(l < r) { ll mid = (l + r) >> 1; if(query(1, 1, 2 * k, l, mid) <= a) r = mid; else l = mid + 1; } if(l > k) l -= k; ++cnt[l]; mx = max(mx, cnt[l]); updata(1, 1, 2 * k, l, a + b); updata(1, 1, 2 * k, l + k, a + b); } bool flag = false; for(int i = 1; i <= k; ++i) { if(cnt[i] == mx) { if(flag) printf(" %d", i - 1); else { printf("%d", i - 1); flag = true; } } } return 0; }