目测某年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(); } }
带着浓重的戾气。

1003

被折叠的 条评论
为什么被折叠?



