A Busiest Computing Nodes
分析:
很明显的线段树维护区间最小值,之后二分查找。
大的方向没问题,主要是很多小的地方妹处理不好.
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;
}