ACM/ICPC 2018亚洲区预选赛北京赛站网络赛D-80 Days--------树状数组
题意就是说1-N个城市为一个环,最开始你手里有C块钱,问从1->N这些城市中,选择任意一个,然后按照顺序绕环一圈,进入每个城市会有a[i]元钱,出来每个城市会有b[i]个城市,问是否能保证经过每个城市,钱都不会能为0,如果可以请输出最小的那个
这题最开始队员想错了。。。后来思路就乱了,整理一下思路,你会发现,其实我们要求的就是从i到n+i的前缀和最小值一定要大于c。
那么如何解决这个问题呢???
你会发现,其实我们只需要算一个影响就行,假如最小的点的在ID,那么前面i->ID的数肯定小,对吧???
如果ID<0,我从i到id,依次减去这个点的贡献,直到ID>=0,那么由于i到ID,ID到N这些数肯定是比ID大的,因此我们只需要看尾部增加的值的是否小于0并且是否小于最小值,如果小于我们,把最小值和最小值的位置更新,这样我们就实现了区间的移动,这样不断移动,我们判断移动是否超过N,如果超过N,证明找不到这个点,如果可以,直接break,输出就可以了。
个人感觉有点莫队的意思。。。
#include<bits/stdc++.h> using namespace std; int t; typedef long long ll; const int maxn = 1e6+5; struct Node { ll a,b,w; } node[maxn<<1]; int n,c; ll tree[maxn<<1]; int lowbit(int x) { return x&(-x); } void add(int x,ll v) { for(int i=x; i<=n; i+=lowbit(i)) { tree[i] += v; } } ll query(int x) { ll ans = 0; for(int i=x; i>0; i-=lowbit(i)) { ans += tree[i]; } return ans; } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&c); memset(tree,0,sizeof(tree)); for(int i=1; i<=n; i++) scanf("%lld",&node[i].a); ll minn = 0x3f3f3f3f3f; int id = 0; ll tmp = c; for(int i=1; i<=n; i++) { scanf("%lld",&node[i].b); node[i].w = node[i].a - node[i].b; node[i+n].w = node[i].w; add(i,node[i].w); add(i+n,node[i].w); tmp += node[i].w; if(tmp < minn) { minn = tmp; id = i; } } int cnt = 1; int len = id; while(minn < 0) { if(cnt > n)break; for(int i=cnt;i<=cnt + len - 1;i++) { minn -= node[i].w; if(minn >= 0) { cnt = i+1; break; } } ll tmp = query(n) - query(cnt-1) + c; minn = 0; for(int i=n+1;i<=n+cnt-1;i++) { tmp += node[i].w; if(tmp < 0) { if(tmp < minn) { minn = tmp; id = i; } } } len = id - cnt + 1; } if(cnt > n)printf("-1\n"); else printf("%d\n",cnt); } }
有不懂欢迎咨询
QQ:1326487164(添加时记得备注)