bzoj2152: 聪聪可可
题目链接
题解
其实本来想找点分的题做,然后就用dp水了
当然,也有好好用点分水一遍的QWQ
dp,维护子树中点到子树跟的距离,%3分类的个数,转移很好写吧 :上,
点分:维护当前树中点到根的距离分类,统计答案好些吧,因为会在当前树的某棵子树下出现非最短路径,容斥一下就好了,下
当然,dp复杂度是更优的
dp
#include<cstdio>
#include<algorithm>
inline int read() {
int x = 0,f = 1;
char c = getchar();
while(c < '0' || c > '9'){if(c == '-')f = -1;c = getchar(); }
while(c <= '9' && c >= '0')x = x * 10 + c - '0',c = getchar();
return x * f;
}
const int maxn = 40007;
struct node {
int v,w,next;
}edge[maxn << 1];
int head[maxn],num = 0;
inline void add_edge(int u,int v,int w) {
edge[++ num].v = v;edge[num].w = w;edge[num].next = head[u]; head[u] = num;
}
int n;
int dp[maxn][3];
int ans = 0;
void dfs(int x,int fa) {
dp[x][0] = 1;
for(int i = head[x];i ;i = edge[i].next) {
int v = edge[i].v;
if(v == fa) continue;
dfs(v,x);
for(int j = 0;j < 3;++ j) {
int tmp = (j + edge[i].w) % 3;
ans += dp[v][j] * dp[x][(3 - tmp) % 3] * 2;
}
//printf("%d\n",ans);
for(int j = 0;j < 3;++ j)
dp[x][(j + edge[i].w) % 3] += dp[v][j];
}
}
int gcd(int x,int y) {
if(!y) return x;else return gcd(y,x % y);
}
int main() {
n = read();
for(int u,v,w,i = 1;i < n;++ i) {
u = read(),v = read(),w = read();
add_edge(u,v,w),add_edge(v,u,w);
}
dfs(1,0);
ans += n;
int g = gcd(ans,n * n);
printf("%d/%d\n",ans / g,n * n/g);
return 0;
}
点分
#include<cstdio>
#include<algorithm>
inline int read() {
int x = 0,f = 1;
char c = getchar();
while(c < '0' || c > '9'){if(c == '-')f = -1;c = getchar(); }
while(c <= '9' && c >= '0')x = x * 10 + c - '0',c = getchar();
return x * f;
}
const int maxn = 40007;
struct node {
int v,w,next;
} edge[maxn << 1];
int head[maxn],num = 0,ans = 0;
bool vis[maxn];
inline void add_edge(int u,int v,int w) {
edge[++ num].v = v;edge[num].w = w;edge[num].next = head[u]; head[u] = num;
}
int n,son[maxn],dis[maxn],cnt[maxn],f[maxn],root,tot;
void get_root(int x,int fa) {
son[x] = 1;f[x] = 0;
for(int i = head[x];i;i = edge[i].next) {
int v = edge[i].v;
if(v == fa || vis[v]) continue;
get_root(v,x);
son[x] += son[v];f[x] = std::max(f[x],son[v]);
}
f[x] = std::max(f[x],tot - son[x]);
if(f[x] < f[root]) root = x;
}
void get_dis(int x,int fa) {
cnt[dis[x]] ++;
for(int i = head[x];i;i = edge[i].next) {
int v = edge[i].v;
if(vis[v] || v == fa) continue ;
dis[v] = (dis[x] + edge[i].w) % 3;
get_dis(v,x);
}
}
int calc(int x,int Dis) {
dis[x] = Dis;cnt[0] = cnt[1] = cnt[2] = 0;
get_dis(x,0);
return cnt[1] * cnt[2] * 2 + cnt[0] * cnt[0];
}
void sol(int x) {
ans += calc(x,0),vis[x] = 1;
for(int i = head[x];i;i = edge[i].next) {
int v = edge[i].v;
if(vis[v]) continue;
ans -= calc(v,edge[i].w);
root = 0;tot = son[v];
get_root(v,0);sol(root);
}
}
int gcd(int x,int y) {
return !y ? x : gcd(y,x % y);
}
int main() {
n = read();
for(int u,v,w,i = 1;i < n;++ i) {
u = read(),v = read(),w = read() % 3;
add_edge(u,v,w),add_edge(v,u,w);
}
tot = n;f[0] = n + 1;
get_root(1,0);
sol(root);
int g = gcd(ans,n * n);
printf("%d/%d\n",ans / g,n * n / g);
return 0;
}