题解:P17746 「RSOI-1C」序列 / sequence

· · 题解

题解是写给自己看的,可能有点看不懂。

记录每个下标可以往后贡献的值,对于每个位置: - 之前可用贡献总和 $\le a_i$。 那么这个位置需要补充 $\text{sum}-a_i$($\text{sum}$ 见代码),并且记录这个差值为其可以往后贡献的值。 ``` ll sum=query(L,i-1); if(sum<=a[i]){ add(i,a[i]-sum);ans+=(a[i]-sum); } ``` - 之前可用贡献总和 $>a_i$。 那么只能保留 $a_i$ 个贡献(这个位置至多加 $a_i$ 次),找到位置 $L$ 使得从 $L$ 开始的贡献刚好达到 $a_i$ 并且修改使得贡献刚好等于 $a_i$(这样就是选择了一段连续地区间 $L\sim i-1$,也就是最右边的区间,这样其产生的贡献可以最持久的贡献到后面)。 ``` else{ while(L<i&&query(L,i-1)>=a[i]){ L++; }L--; add(L,a[i]-query(L,i-1)); } ``` 树状数组/线段树维护。 ## 完整代码: ``` #include<bits/stdc++.h> using namespace std; #define ll long long const ll N=1000005; ll n,k; ll t1[N],t2[N],a[N]; ll w[N],tag[N],L=1,ans=0,zl=0; void add(ll x,ll y){ if(x)for(int i=x;i<=n;i+=(i&(-1*i)))w[i]+=y; } ll query(ll l,ll r){ ll s2=0,s1=0; if(r!=0)for(int i=r;i;i-=(i&(-1*i)))s2+=w[i]; if(l!=0)for(int i=l-1;i;i-=(i&(-1*i)))s1+=w[i]; return s2-s1; } int main(){ scanf("%lld %lld",&n,&k); for(int i=1;i<=n;i++){scanf("%lld",&t1[i]);} for(int i=1;i<=n;i++){scanf("%lld",&t2[i]);} for(int i=1;i<=n;i++){ a[i]=t2[i]-t1[i]; if(a[i]<0){cout<<"-1\n";return 0;} } for(int i=1;i<=n;i++){ L=max(L,i-k+1); ll sum=query(L,i-1); if(sum<=a[i]){ add(i,a[i]-sum);ans+=(a[i]-sum); }else{ while(L<i&&query(L,i-1)>=a[i]){ L++; }L--; add(L,a[i]-query(L,i-1)); } } cout<<ans<<endl; return 0; } ```