灯 - 分块

题目大意:有一列灯,每个灯有颜色。每次操作形如将某种颜色的灯全部点亮或者熄灭,问亮着的灯的段数。一开始全灭。 n , q 1 0 5 n,q\le10^5
题解:显然先把相邻颜色相同的球扔掉,然后用点数减边数统计段数(连通块数),点数显然,考虑边数。转为一个对颜色建点的图,两点连边边权表示这两种颜色有多少灯是相邻的,那么边数是O(n)的。然后每次就是加入或者删除一个点求生成子图边权之和。
显然按照度数分块,度数小的叫A,否则叫B。B的数量不超过根号。对每个B维护来自亮着的A的入边边权之和,每次加入删除A就暴力加入删除,否则枚举所有B看是否是亮着的以及连边边权是多少。复杂度 O ( n n ) O(n\sqrt n)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=100010;
vector<pii> g[N],gB[N];
int frA[N],opn[N],a[N],isA[N],cnt[N];
inline int solve(vector<pii> &g)
{
    int n=(int)g.size();if(!n) return 0;
    vector<pii> ans;ans.resize(n);int k=1;
    sort(g.begin(),g.end());ans[0]=g[0];
    rep(i,1,n-1)
        if(g[i]==g[i-1]) ans[k-1].sec++;
        else ans[k++]=g[i];
    return ans.resize(k),g=ans,0;
}
int main()
{
    int n=inn(),m=inn(),q=inn(),lim=(int)sqrt(n+0.5);
    rep(i,1,n) a[i]=inn();int ns=n;n=0;
    rep(i,1,ns) if(a[i]^a[i-1]) a[++n]=a[i];
    rep(i,1,n) cnt[a[i]]++;
    rep(i,1,n-1) g[a[i]].pb(mp(a[i+1],1)),g[a[i+1]].pb(mp(a[i],1));
    rep(i,1,m) solve(g[i]);
    rep(i,1,m) if((int)g[i].size()<lim) isA[i]=1;
    rep(x,1,m) if(!isA[x]) Rep(i,g[x])
        if(!isA[g[x][i].fir]) gB[x].pb(g[x][i]);
    int AA=0,AB=0,BB=0,tot=0;
    while(q--)
    {
        int x=inn(),s=(opn[x]?-1:1),y;
        tot+=s*cnt[x],opn[x]^=1;
        if(isA[x])
        {
            Rep(i,g[x])
                if(isA[y=g[x][i].fir]) AA+=(opn[y]?s*g[x][i].sec:0);
                else frA[y]+=s*g[x][i].sec,AB+=(opn[y]?s*g[x][i].sec:0);
        }
        else{
            AB+=s*frA[x];
            Rep(i,gB[x]) if(opn[gB[x][i].fir]) BB+=s*gB[x][i].sec;
        }
        printf("%d\n",tot-AA-AB-BB);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/88663660