【SCAU 19校賽】18434 Painting Walls 差分 哈希 詳解

Description
There are n walls in Sweetzero’s house, numbered from 1 to n. The i-th wall has a value Wi.

Sweetzero’s friend Takio is a naughty boy. Every day, Takio will cast a magic on Sweetzero’s house to make all the walls unpainted, then Sweetzero
will use a magic pen to repaint the walls.

The magic pen can be used to paint continued walls at one time. Although Sweetzero can use that pen one time to paint all the walls by painting [1, n],
he enjoys using that pen and cast it several times.
Then after Sweetzero painted the walls at the ith day, Takio will ask Sweetzero the summary value of walls being painted exactly Ki times that day.
Sweetzero is cool and doesn’t want to answer these silly questions.
So, please tell Takio the answer for every day. Specially if there are no walls being painted exactly Ki times, the answer is zero.

輸入格式
The first line contains one integer T (1 <= T <= 20) indicating the number of test cases.

For each test case, the first line contains an integer n (1 <= n <= 1000000), the number of walls and D (1 <= D <= 100000), the number of days Takio
clean Sweetzero’s walls.
The second line contains n integers, from W1 to Wn ((0 <= W1 … Wn <= 1000000000)), indicating the value of walls.
Then followed by several lines describing D days.
The first line for the i-th day contains two integers Mi (1 <= Mi <= 100000) and Ki (0 <= Ki <= 100000),
which means Sweetzero uses his magic pen Mi times and Takio asks the walls painted exactly Ki times.
Then comes Mi lines, each line contains two integers L and R, (1 <= L <= R <= n) represents a usage of the magic pen, painted from the L-th wall to the R-th.
You can assume that tjhe sum of all Mi for all test cases <= 600000.

輸出格式
For each test case, output D lines.
Each line contains the answer for the i-th day.

輸入樣例
1
5 2
1 2 3 4 5
2 1
1 3
2 3
2 2
1 4
2 5

輸出樣例
1
9

題意:給一個序列若干詢問,每次詢問對m個區間計數+1,求最後計數等於k的區間元素和。

思路(差分):

思路:差分和哈希
1.首先發現,題目簡化後的邏輯表達就是每次詢問讓你給區間【L,R】內的值+1,然後每次詢問完回答該操作完後有哪些區間被覆蓋了k次,輸出對應區間和

2.對於區間加和問題,可能會往線段樹或者樹狀數組的方向想,但是這個題明確是每次+1,就相當於區間的計數,所以可以往差分的方向想。

3.構建差分數組b[i] = a[i] - a[i-1],它有什麼特點呢?一個是差分數組的i位置前綴和就是對應a[i](自行證明),另一個就是本題關鍵——我要讓【L,R】區間內的值+1,只需要b[L]++, b[R+1]–即可。(爲什麼?就相當於這個區間內部差分不變,因爲是同時+1。而對於區間左邊界相當於比前一個數多了一,右邊界比後一個數少1)。

4.然後,用map存這些區間端點(自帶按照鍵大小排序),這個時候我們只需要遍歷一遍這些詢問到的端點,用變量cur += b[i],表示當前區間的詢問次數。
比如:詢問【1,10】,【3,5】。通過上述知道
b[1] = 1 , b[3] = 1 , b[6] = -1, b[11] = -1,
假設本次詢問 k=2 . 那麼在遍歷一遍詢問到的區間端點時,
1).cur += b[1] => 1
2).cur += b[3] => 2 注意,此時cur等於k,說明這個點開始的區間已經滿足題意了。但我們現在不知道這個區間多長,就先標記着 flag = 1
3).cur += b[6] => 1 哦吼,發現cur變了,這個時候我們就知道前面滿足題意的區間是多長了,就是i - pre(前一個端點)+ 1 。這個時候答案就是ans += sum[i-1] - sum[pre-1]。同時flag=0
4).cur += b[11] => 0 發現沒啥玩意了。不計數。

5.主體思路便是如上。最後注意一個非常噁心的點。k可以取0,這個時候要反過來求!

AC代碼:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e6+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll b[maxn];
ll a[maxn];
ll sum[maxn];
map<ll,ll> Map;

int main()
{
   // freopen("DATA.txt","r",stdin);
    ll kase;
    kase = read();
    while(kase--)
    {
        ll n,d;
        n = read(); d = read(); rep(i,0,n) sum[i] = 0;
        rep(i,1,n) a[i] = read(), sum[i] = sum[i-1] + a[i];
        rep(i,1,d)
        {
            Map.clear();
            ll m, k;
            m = read(); k = read();
            if(k)
            {
                 rep(j,1,m)
                {
                    ll L, R;
                    L = read(); R = read();
                    b[L] ++ , b[R+1] --;
                    Map[L] = Map[R+1] = 1;
                }
                ll cur = 0; ll pre = 1; ll ans = 0; bool flag = 0;
                for(map<ll,ll>:: iterator it = Map.begin(); it!=Map.end();it++)
                {
                    cur += b[it->fi];
                    if(flag) ans += sum[it->fi-1] - sum[pre-1];
                    if(cur==k)
                    flag = 1;
                    else flag = 0;
                    pre = it->fi;
                }
                printf("%lld\n",ans);
                for(map<ll,ll>:: iterator it = Map.begin(); it!=Map.end();it++) b[it->fi] = 0;
            }
            else
            {
                    rep(j,1,m)
                {
                    ll L, R;
                    L = read(); R = read();
                    b[L] ++ , b[R+1] --;
                    Map[L] = Map[R+1] = 1;
                }
                ll cur = 0; ll pre = 1; ll ans = 0; bool flag = 0;
                for(map<ll,ll>:: iterator it = Map.begin(); it!=Map.end();it++)
                {
                    cur += b[it->fi];
                    if(flag) ans += sum[it->fi-1] - sum[pre-1];
                    if(cur)
                    flag = 1;
                    else flag = 0;
                    pre = it->fi;
                }
                ans = sum[n] - sum[0] - ans;
                printf("%lld\n",ans);
                for(map<ll,ll>:: iterator it = Map.begin(); it!=Map.end();it++) b[it->fi] = 0;
            }

        }
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章