四题200多名吧
反正就只能切水题
1001
求2^m次方有多少位,一开始以为是找规律,但发现有误差,后来发现用公式变成log可以直接算出来。
1002
很坑,wa了很多次。但是思路一开始就是对了,算出各个字母的贡献值,然后排序,不过一开始没注意前导0
1006
置换群,然后xzn写完,帮他查了很多弱智错误。。然后过了
1011
找规律 我们可以发现是 (n 1 2 3 4 … ..n-2) + (n-1 1 2 3 ….n-2)的循环节。
然后敲了一发,就过了。
补题:
1003
题意:一棵树中每个节点都有一个颜色,求所有路径的sum。
每条路径值 = 这条路径经过的颜色种数。
这个问题的路径其实是可以分割的,比赛时一直以为不可以分割,所以不会。
既然可以分割,必然也不需要去枚举路径。
比如
1
\
2
\
3
这样一棵树,颜色1 经过他的路径条数有2条
颜色2 经过他的路径条数有3条
颜色3经过他的路径条数有2条 所以ans=7
1
\
2
/ \
1 3
这样一棵树,颜色1 经过他的路径条数有2+3条
颜色2 经过他的路径条数有3+3条
颜色3经过他的路径条数有2+1条 所以ans=14
我们可以再列举几个例子,仔细想想,这个推断竟然是成立的,惊了!
再仔细想想,其实是有道理的,因为每条路径上的每种颜色对答案的贡献值其实是1
所以对于每一种颜色来说,贡献值就是有多少条路径通过这一种颜色。
但是统计这个是很有可能重复统计的,所以我们反过来思考,有多少条路径没有经过某种颜色
然后我们跑出dfs序 每一种颜色(可能有多个点) 会把树 划分成许多块。
然后我们那总路径数,减去这些不经过这种颜色的路径数。
然后我们再看标程,哇,写的是真的好,这是世界上最难学的算法就是dfs。
typedef long long LL;
const int maxn = 200001;
int n, c[maxn], lnk[maxn], pos[maxn], ctr[maxn], rem[maxn];
//rem 存的是 当前节点所有子节点数 (当某颜色上面的节点没有当前颜色节点时,上面的所有节点块形成的每条路径不不经过这种颜色。
//ctr 存在的是下一个相同颜色节点 的 子节点个数。
LL ans;
struct Edge {
int nxt, v;
} e[maxn << 1 | 1];
inline LL sum2(int x) {
return (x * (x - 1LL)) >> 1;
}
int dfs(int u, int fa) {
int su = 1, o = pos[c[u]]; // pos[c[u]] 当前颜色在dfs中上一个当前颜色的节点号
pos[c[u]] = u; // 颜色x的位置更新为u,当前的
for(int it = lnk[u]; it; it = e[it].nxt) // 遍历
if(e[it].v != fa) {
ctr[u] = 0; // ctr 要初始化为0,不然dfs与u相同的颜色时会累加到ctr[u]上来
int sv = dfs(e[it].v, u); // e[it].v这一块子树的节点数。不包括u
ans -= sum2(sv - ctr[u]); //(e[it].v这一块子树)所有子节点数-下一个相同颜色节点的子节点数= 他们之间的连通块的数目。
// 还有一个忽略的地方: 如果ctr[u]=0,那么说明下面没有相同颜色,所以下面的块形成的路径也应该减去。
su += sv;
}
// (o ? ctr[o] : rem[c[u]]) += su;
/* 走到这一步时,dfs已经把u的所有的子树都扫过一遍了。
如果o为真,代表前面最近出现c[u],节点号为o,且o 不可能是子树中出现的
所以此时,当前节点 的所有子节点数(包括自己) 都加到 上一个节点o的ctr 中
*/
if(o) ctr[o]+=su; //
else rem[c[u]]+=su; //如果上面没出现过, 那么他所有的子树放进rem[当前颜色](把自己也放进去)
pos[c[u]] = o; // 还原当前颜色的节点号,所以每一个的o 都必然是上面最近的颜色x的位置。
return su; //返回包括自己+所有子树的节点数
}
int main() {
//freopen("1.txt","r",stdin);
for(int Case = 1; scanf("%d", &n) == 1; ++Case) {
for(int i = 1; i <= n; ++i) {
scanf("%d", c + i);
lnk[i] = rem[i] = 0;
}
for(int i = 1, u, v; i < n; ++i) {
scanf("%d%d", &u, &v);
e[i << 1] = (Edge){lnk[u], v};
lnk[u] = i << 1;
e[i << 1 | 1] = (Edge){lnk[v], u};
lnk[v] = i << 1 | 1; // 链式存图
}
ans = sum2(n) * n; // 一共有n(n-1)/2条路径,假设每条路径都经过n?
// printf("%I64d\n",ans);
dfs(1, -1);
// printf("%I64d\n",ans);
for(int i = 1; i <= n; ++i) // 遍历所有节点
ans -= sum2(n - rem[i]);
printf("Case #%d: %I64d\n", Case, ans);
}
return 0;
}