这题有两种作法,一开始我想的是点分治,类似于POJ1741那题(点这里),我们按同样的方法搜索 dep 并记录,不过这次记录的是 dep%3=0,1,2 的数量,记作 t[i] 。那么根据乘法原理, ans1=t[1]∗t[2]∗2+t[0]∗t[0] ,然后再算出儿子的 ans2 ,并减去即可。求出满足要求的有序点对数后,概率即可求。由于每一层里不用排序,只用搜一遍,这种方法的时间复杂度是 O(nlogn) 的。
然而对于这样的题,我们只关心 dep%3 的数量,故可以用树形dp。记 f[x][i] 为以 x 为根的子树中到 x 的深度 mod 3 为 i 的节点数。然后就是从儿子到根的简单的转移,讨论连接的这条边 l%3 , i=0 对应要加的是 (3−l%3)%3 ,其他的类推。在转移之前先递归得到儿子的状态,并计算出当前对答案的贡献,有点类似于treap+启发式合并,都是根和一些子树看作一个整体,并与当前的子树看作两部分,计算之间的答案,这里的路径都是保证过当前根的,不会算重算漏。由于 dep 的值只有 0,1,2 这个性质,所以直接记掉所有状态,用树形DP就可以 O(n) 完美解决。
#include
#include
#include
#include
#include
#include
#define N 20005
#define mod 5
using namespace std;
int n, cur = -1, head_p[N], f[N], sum, t[mod], dep[N], siz[N], ans, root;
bool vis[N];
struct Adj{int next, obj, len;} Edg[N<<1];
void Insert(int a, int b, int c){
Edg[++cur].next = head_p[a];
Edg[cur].obj = b;
Edg[cur].len = c;
head_p[a] = cur;
}
void Getroot(int x, int fa){
siz[x] = 1; f[x] = 0;
for(int i = head_p[x]; ~ i; i = Edg[i].next){
int v = Edg[i].obj;
if(v == fa || vis[v]) continue;
Getroot(v, x);
siz[x] += siz[v];
f[x] = max(f[x], siz[v]);
}
f[x] = max(f[x], sum - siz[x]);
if(f[x] < f[root]) root = x;
}
void Getdeep(int x, int fa){
t[dep[x]] ++;
for(int i = head_p[x]; ~ i; i = Edg[i].next){
int v = Edg[i].obj, l = Edg[i].len;
if(v == fa || vis[v]) continue;
dep[v] = (dep[x] + l) % 3;
Getdeep(v, x);
}
}
int Calc(int x, int v){
t[0] = t[1] = t[2] = 0;
dep[x] = v % 3;
Getdeep(x, 0);
return t[0] * t[0] + t[1] * t[2] * 2;
}
void Solve(int x){
ans += Calc(x, 0);
vis[x] = true;
for(int i = head_p[x]; ~ i; i = Edg[i].next){
int v = Edg[i].obj, l = Edg[i].len;
if(vis[v]) continue;
ans -= Calc(v, l);
sum = siz[v];
root = 0;
Getroot(v, 0);
Solve(root);
}
}
int Gcd(int a, int b){
if(!b) return a;
return Gcd(b, a % b);
}
int main(){
freopen("bzoj2512.in", "r", stdin);
freopen("bzoj2512.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i++) head_p[i] = -1;
int a, b, c;
for(int i = 1; i < n; i++){
scanf("%d%d%d", &a, &b, &c);
Insert(a, b, c);
Insert(b, a, c);
}
f[0] = sum = n;
root = 0;
Getroot(1, 0);
Solve(root);
int gg = Gcd(ans, n * n);
printf("%d/%d\n", ans / gg, n * n / gg);
return 0;
}
#include
#include
#include
#include
#include
#include
#define N 20010
using namespace std;
int n, cur = -1, head_p[N], f[N][3], ans;
struct Adj{int next, obj, len;} Edg[N<<1];
void Insert(int a, int b, int c){
Edg[++cur].next = head_p[a];
Edg[cur].obj = b;
Edg[cur].len = c;
head_p[a] = cur;
}
void Dp(int x, int fa){
ans ++;
f[x][0] = 1;
for(int i = head_p[x]; ~ i; i = Edg[i].next){
int v = Edg[i].obj, l = Edg[i].len % 3;
if(v == fa) continue;
Dp(v, x);
ans += (f[x][0] * f[v][(3-l)%3] + f[x][1] * f[v][(2-l)%3] + f[x][2] * f[v][(4-l)%3]) << 1;
f[x][0] += f[v][(3-l)%3];
f[x][1] += f[v][(4-l)%3];
f[x][2] += f[v][(2-l)%3];
}
}
int Gcd(int a, int b){
if(!b) return a;
return Gcd(b, a % b);
}
int main(){
freopen("bzoj2512.in", "r", stdin);
freopen("bzoj2512.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i++) head_p[i] = -1;
int a, b, c;
for(int i = 1; i < n; i++){
scanf("%d%d%d", &a, &b, &c);
Insert(a, b, c);
Insert(b, a, c);
}
Dp(1, 0);
int gg = Gcd(ans, n * n);
printf("%d/%d\n", ans / gg, n * n / gg);
return 0;
}
春天 马上就要来了
让我与你相遇的春天 就要来了
再也没有你的春天 就要来了
——《四月是你的谎言》