bzoj 2805: [Ctsc2012]Circuit 物理

        物理太差被虐了/(ㄒoㄒ)/~~

        首先将电阻看成1欧不影响答案吧。。

        考虑将一个点x的电势Ux,它父亲y的电势Uy,考虑将Ux用aUy+b的形式来表示,这样就可以利用树高为50的条件O(H)查询修改了。

        对于任意一对点(x,y),考虑y->x的电流为(Uy-Ux+E)/R,忽略单位的话在数值上可以看成Uy-Ux+E。其中E为y->x的路上的电源带来的电势。那么根据基尔霍夫定律显然有ΣUy-Ux+E=0。而对于x的某一个儿子y,显然一直Uy=aUx+b,那么借助这个式子我们就可以推出x相对于x的父亲的a,b。注意到修改x只会影响x到根的a,b,因此记录一下某个点x到儿子的E的和就可以O(H)修改了。

        另外发现,修改操作实际上是对某一条边x->y上面的E的修改,对任一点的a是没有影响的;因此预处理a然后修改b即可。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 50005
using namespace std;

int n,m,tot,fst[N],pnt[N<<1],nxt[N<<1],dgr[N],u[N],v[N],fa[N],q[N];
double a[N],b[N],s[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void add(int x,int y){
	dgr[y]++; pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x){
	int p; a[x]=dgr[x];
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa[x]){
			fa[y]=x; dfs(y);
			a[x]-=a[y];
		}
	}
	a[x]=1/a[x];
}
void ins(int x,int z){
	int y=fa[x]; u[x]+=z; v[y]+=z;
	for (; x<n; x=y,y=fa[y]){
		s[y]-=b[x];
		b[x]=(s[x]+v[x]-u[x])*a[x];
		s[y]+=b[x];
	}
}
void qry(int x){
	int tp=0,i; double ans=0;
	for (; x<n; x=fa[x]) q[++tp]=x;
	for (i=tp; i; i--) ans=ans*a[q[i]]+b[q[i]];
	printf("%.12f\n",ans);
}
int main(){
	n=read(); m=read(); int i,x,y,z;
	for (i=1; i<n; i++){
		x=read(); y=read();
		add(x,y); add(y,x);
	}
	for (i=1; i<=n; i++)
		if (dgr[i]==1) break;
	add(i,++n); add(n,i);
	for (i=1; i<n; i++) if (dgr[i]==1) dgr[i]++;
	dfs(n);
	char ch;
	while (m--){
		ch=getchar(); while (ch<'A' || ch>'Z') ch=getchar();
		if (ch=='C'){
			x=read(); y=read(); z=read();
			if (x==fa[y]){
				swap(x,y); z=-z;
			}
			ins(x,z);
		} else qry(read());
	}
	return 0;
}


by lych

2016.5.23

你可能感兴趣的:(DFS,物理,基尔霍夫定律)