BZOJ3648: 寝室管理

比较裸的点分,因为图是由一棵树加一条边组成,所以只会有一个环,找到这个环然后随便拿掉一条边做点分,然后再考虑经过这条边的路径,沿着环走一圈就行了


#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;

const int maxn = 210000;
struct edge
{
    int y,nex;
    edge(){}
    edge(int _y,int _nex){y=_y;nex=_nex;}
}a[maxn]; int len,fir[maxn];
void ins(int x,int y){a[++len]=edge(y,fir[x]);fir[x]=len;}
int n,m,K;
bool cc[maxn];
int siz[maxn];

int t[maxn],tp=0,circle[maxn],clen,ce,ce2;
bool find_circle(int x,int f)
{
    t[++tp]=x;
    if(cc[x])
    {
        clen=0;
        while(t[tp-1]!=x) circle[++clen]=t[tp--];
        circle[++clen]=t[tp];
        return true;
    }
    cc[x]=true;
    for(int k=fir[x];k;k=a[k].nex)
    {
        int y=a[k].y;
        if(y!=f) 
        {
            if(find_circle(y,x))return true;
        }
    }
    cc[x]=false;
    tp--;
    return false;
}
void find_root(int x,int f,int &rt,int sum)
{
    siz[x]=1;
    bool flag=true;
    for(int k=fir[x];k;k=a[k].nex)
    {
        if(k==ce||k==ce2)continue;
        int y=a[k].y;
        if(y!=f&&!cc[y])
        {
            find_root(y,x,rt,sum);
            siz[x]+=siz[y];
            if(siz[y]*2>sum) flag=false;
        }
    }
    if((sum-siz[x])*2>sum) flag=false;
    if(flag) rt=x;
}
int s[maxn],v[maxn],vlen;
ll ret;
void dfs(int x,int f,int r,int pos)
{
    r++;
    s[++tp]=r;
    while(pos<vlen&&r+1>=v[pos+1]) pos++;
    if(r+1>=v[pos]) ret+=(ll)pos;
    for(int k=fir[x];k;k=a[k].nex)
    {
        if(k==ce||k==ce2)continue;
        int y=a[k].y;
        if(y!=f&&!cc[y]) dfs(y,x,r,pos);
    }
}
void solve(int x,int sum)
{
    find_root(x,0,x,sum);
    cc[x]=true;
    v[vlen=1]=K;
    for(int k=fir[x];k;k=a[k].nex)
    {
        if(k==ce||k==ce2)continue;
        int y=a[k].y;
        if(!cc[y])
        {
            tp=0; dfs(y,x,0,0);
            for(int i=1;i<=tp;i++) v[++vlen]=K-s[i];
            sort(v+1,v+vlen+1);
        }
    }
    vlen=0;
    for(int k=fir[x];k;k=a[k].nex)
    {
        if(k==ce||k==ce2)continue;
        int y=a[k].y;
        if(!cc[y])
        {
            int tt=siz[y]>siz[x]?sum-siz[x]:siz[y];
            solve(y,tt);
        }
    }
}
bool mark[maxn];
vector<int>u[maxn];
void build_v(int rt,int x,int f,int r)
{
    r++;
    u[rt].push_back(r);
    for(int k=fir[x];k;k=a[k].nex)
    {
        int y=a[k].y;
        if(y!=f&&!mark[k]) build_v(rt,y,x,r);
    }
}
int add(int *qs,int x,int c)
{
    x=max(K-x+1,1);
    while(x<=n){qs[x]+=c;x+=lowbit(x);}
}
ll qy(int *qs,int x)
{
    x++;
    ll rr=0; for(;x;x-=lowbit(x))rr+=(ll)qs[x];
    return rr;
}

int main()
{
    memset(fir,0,sizeof fir); len=1;
    ce=ce2=0; ret=0;
    scanf("%d%d%d",&n,&m,&K);
    if(K==0){printf("0\n"); return 0;}
    for(int i=1;i<=m;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        ins(x,y); ins(y,x);
    }
    if(n==m)
    {
        find_circle(1,0); 
        for(int k=fir[circle[1]];k;k=a[k].nex)
            if(a[k].y==circle[2]) {ce=k;break;}
        ce2=ce^1;
        memset(cc,false,sizeof cc);
    }
    solve(1,n);
    if(n==m)
    {
    for(int i=1;i<=clen;i++)
    {
        int x=circle[i];
        for(int k=fir[x];k;k=a[k].nex)
        {
            if(i==1)
            {
                if(a[k].y==circle[2]||a[k].y==circle[clen])
                    mark[k]=true;
            }
            else if(i==clen)
            {
                if(a[k].y==circle[1]||a[k].y==circle[clen-1])
                    mark[k]=true;
            }
            else
            {
                if(a[k].y==circle[i-1]||a[k].y==circle[i+1])
                    mark[k]=true;
            }
        }
        build_v(x,x,x,0);
    }
    memset(v,0,sizeof v);
    for(int i=2;i<=clen;i++)
    {
        int x=circle[i];
        for(int j=0;j<u[x].size();j++)
        {
            int ts=u[x][j];
            add(v,ts+clen-i+1,1);
        }
    }
    for(int i=0;i<u[circle[1]].size();i++)
        add(v,u[circle[1]][i],1);

    for(int i=2;i<=clen;i++)
    {
        int x=circle[i]; tp=0;
        for(int j=0;j<u[x].size();j++) t[++tp]=u[x][j];
        for(int j=1;j<=tp;j++) add(v,t[j]+clen-i+1,-1);
        for(int j=1;j<=tp;j++) ret+=qy(v,t[j]+i-2);
    }
    }
    printf("%lld\n",ret);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值