HDU3844Mining Your Own Business

目测某年HNOI,(其实这个题是2011年的WF,hdu上找到的,HNOI2012那个中文题在bzoj和loj上都有,叫矿场搭建,题意几乎一样,数据比较弱,交这份代码也能A)。

先讲题解,然后说一些有(e)趣(xin)的事情。

首先肯定是个点双,先求一下点双。

然后我们分析一下。

当这个点双里没割点时(一个大号的孤立点双),那么我们要建两个特殊节点,因为一个塌了可以去另一个,对第1问贡献为2,第2问贡献贡献为C(n,2)=(n-1)*n/2。(n是点双大小哦)。

当这个点双有一个割点(一个叶子),那么我们在除割点以外的地方建特殊节点,因为割点塌了去自家的,自家的塌了走割点去隔壁的。对第1问贡献为1,第2问贡献为n-1。(n是点双大小哦)。

当这个点双有两个及以上割点时,别建,天塌下来有两个及以上隔壁顶着。无贡献。

多测,注意清空。

下面说一些题外话。

这个题码完以后去bzoj交那个中文题,WA了,交loj,70,一看,错的是那三个小点,大点都过了……后来发现T清零了……case一个1,若干0,赶紧改,一交A了,回bzoj,A了。

然后交自家oj,RE,一看,数组可能要2倍,开完一交,又RE,一直交,一直RE,我去,这数据也TQL,一想,可能挂在外网上,min和max都改手打,还是RE,到处窜座去问,没人能给出解答(因为我是倒着刷的),然后去找啾啾,他上hdu给我找了一手,找到了,交,TLE,3000,一个测试点?WF?这么那啥。静态调错,init里边数没清零……明白了,那两个A的是因为数据太小,我的数组开的太大,不清空边数都没炸……那WF的题5e4的大范围,不出5个就卡死了,赶紧改,交,WA了。

静态调错,ans2*=写的赋值,点双大小写的n(知道我上文为什么要强调了吗?我推的时候拿n推的,打的时候忘了),改,交,A了。

现在又明白一个问题,bz和loj上的那道题,只有一个点双就是一个大的点双,没有孤立的,所以上面的错误就是正确的。

所以,做完这个题就不要去做那个题了,没意思了。(试了试,ans2*=C(n,2)也能A,有点迷,难不成WF也默认了?)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
int read(){
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9'){
        if(x=='-') f=-1;
        x=getchar();
    }while(x>='0'&&x<='9'){
        sum=sum*10+x-'0';
        x=getchar();
    }return sum*f;
}
struct EDGE{
    int ed,nex;
}edge[205000];
int first[205000],num;
int n,m,root;
int dfn[205000],low[205000],sta[2050000];
int ord,top,vccnum,T;
vector<int>vcc[205000];
long long ans1=0,ans2=1;
bool cut[205000];
inline int max(int a,int b){
    return a>b?a:b;
}
inline int min(int a,int b){
    return a<b?a:b;
}
void init(){
    memset(edge,0,sizeof(edge));
    memset(first,0,sizeof(first));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(cut,0,sizeof(cut));
    for(int i=1;i<=vccnum;i++) vcc[i].clear();
    ord=top=num=vccnum=ans1=root=n=0;
    ans2=1;
}

void tarjan(int x){
    dfn[x]=low[x]=++ord;
    sta[++top]=x;
    if(x==root&&first[x]==0){
        vcc[++vccnum].push_back(x);
        return ;
    }
    int child=0;
    for(int i=first[x];i;i=edge[i].nex){
        int y=edge[i].ed;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x]){
                child++;
                if((x==root&&child>=2)||(x!=root&&child>0)) cut[x]=1;
                vcc[++vccnum].push_back(x);
                int p;
                do{
                    p=sta[top--];
                    vcc[vccnum].push_back(p);
                }while(p!=y);
            }
        }else low[x]=min(low[x],dfn[y]);
    }
}
void add(int st,int ed){
    edge[++num].ed=ed;
    edge[num].nex=first[st];
    first[st]=num;
}
int main(){
//    freopen("c.in","r",stdin);
    m=read();
    while(m!=0){
        T++;
        for(int i=1,x,y;i<=m;i++){
            x=read();y=read();
            add(x,y);add(y,x);
            n=max(n,x);n=max(n,y);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i]) root=i,tarjan(i);
        for(int i=1;i<=vccnum;i++){
            int sum=0;
            for(int j=0;j<vcc[i].size();j++)
                if(cut[vcc[i][j]])
                    sum++;
            if(!sum){
                ans1+=2;
                ans2*=vcc[i].size()*(vcc[i].size()-1)/2;
            }
            else if(sum==1){
                ans1++;
                ans2*=1ll*(vcc[i].size()-1);
            }
        }
        printf("Case %d: %lld %lld\n",T,ans1,ans2);
        m=read();init();
    }
}
View Code

 带着浓重的戾气。

转载于:https://www.cnblogs.com/Yu-shi/p/11182094.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值