[BZOJ3514]Codechef MARCH14 GERALD07加强版 题解
题目地址:BZOJ& …
May all the beauty be blessed.
题目地址:洛谷:【P2634】[国家集训队]聪聪可可 – 洛谷、BZOJ:Problem 2152. — 聪聪可可
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。
他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。
聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。
输入格式:
输入的第1行包含1个正整数n。后面n-1行,每行3个整数x、y、w,表示x号点和y号点之间有一条边,上面的数是w。
输出格式:
以即约分数形式输出这个概率(即“a/b”的形式,其中a和b必须互质。如果概率为1,输出“1/1”)。
输入样例#1:
5 1 2 1 1 3 2 1 4 1 2 5 3
输出样例#1:
13/25
直接把距离、边权对3取模存起来,这样可以减少模运算的次数,优化常数。
统计一个子树下点的距离对3取余为0、1、2的点有多少,答案即c_0^2 + 2c_1c_2(距离被3整除的点之间以及和根,距离余3为1、2的点互相构成点对)。然后把不合法方案减掉,套个gcd,就解决了。
// Code by KSkun, 2018/3
#include <cstdio>
inline int max(int a, int b) {
return a > b ? a : b;
}
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int readint() {
register int res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 20005, INF = 1e9;
int n, m, ut, vt, wt, k, rt, res = 0;
int siz[MAXN], dep[MAXN], msz[MAXN], sum;
bool vis[MAXN];
struct Edge {
int to, w, nxt;
} gra[MAXN << 1];
int head[MAXN], tot = 0;
inline void addedge(int u, int v, int w) {
tot++;
gra[tot].to = v;
gra[tot].w = w;
gra[tot].nxt = head[u];
head[u] = tot;
}
int c[3];
inline void getroot(int u, int fa) {
siz[u] = 1;
msz[u] = 0;
for(int i = head[u]; i; i = gra[i].nxt) {
int v = gra[i].to;
if(vis[v] || v == fa) continue;
getroot(v, u);
siz[u] += siz[v];
msz[u] = max(msz[u], siz[v]);
}
msz[u] = max(msz[u], sum - siz[u]);
if(msz[u] < msz[rt]) rt = u;
}
inline void caldep(int u, int fa) {
c[dep[u]]++;
for(int i = head[u]; i; i = gra[i].nxt) {
int v = gra[i].to, w = gra[i].w;
if(vis[v] || v == fa) continue;
dep[v] = (dep[u] + w) % 3;
caldep(v, u);
}
}
inline int work(int u, int w) {
c[0] = c[1] = c[2] = 0;
dep[u] = w;
caldep(u, 0);
return c[0] * c[0] + c[1] * c[2] * 2;
}
inline void dfs(int u) {
res += work(u, 0);
vis[u] = true;
for(int i = head[u]; i; i = gra[i].nxt) {
int v = gra[i].to, w = gra[i].w;
if(vis[v]) continue;
res -= work(v, w);
rt = 0;
sum = siz[v];
getroot(v, 0);
dfs(rt);
}
}
inline int gcd(int a, int b) {
int t;
while(t = a % b) {
a = b; b = t;
}
return b;
}
int main() {
n = readint();
for(int i = 1; i < n; i++) {
ut = readint(); vt = readint(); wt = readint() % 3;
addedge(ut, vt, wt);
addedge(vt, ut, wt);
}
rt = 0;
msz[0] = INF;
sum = n;
getroot(1, 0);
dfs(rt);
int t1 = n * n, t2 = gcd(res, t1);
printf("%d/%d", res / t2, t1 / t2);
return 0;
}
题目地址:BZOJ:Problem 3589. — 动态树
小明在楼下种了一棵动态树, 该树每天会在某些节点上长出一些果子. 这棵树的根节点为1, 它有n个节点, n-1条边.
别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件
事件0:
这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子.
事件1:
小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次.
初始时, 每个节点上都没有果子.
输入格式:
第一行一个整数n(1<=n<=200,000), 即节点数.
接下来n-1行, 每行两个数字u, v. 表示果子u和果子v之间有一条直接的边. 节点从1开始编号.
在接下来一个整数nQ(1<=nQ<=200,000), 表示事件.
最后nQ行, 每行开头要么是0, 要么是1.
如果是0, 表示这个事件是事件0. 这行接下来的2个整数u, delta表示以u为根的子树中的每个节点长出了delta个果子.
如果是1, 表示这个事件是事件1. 这行接下来一个整数K(1<=K<=5), 表示这次询问涉及K个树枝. 接下来K对整数u_k, v_k, 每个树枝从节点u_k到节点v_k. 由于果子数可能非常多, 请输出这个数模2^31的结果.
输出格式:
对于每个事件1, 输出询问的果子数.
输入样例#1:
5 1 2 2 3 2 4 1 5 3 0 1 1 0 2 3 1 2 3 1 1 4
输出样例#1:
13
1 <= n <= 200,000, 1 <= nQ <= 200,000, K = 5.
生成每个树枝的过程是这样的:先在树中随机找一个节点, 然后在这个节点到根的路径上随机选一个节点, 这两个节点就作为树枝的两端.
又是树链信息,套树剖。考虑线段树维护区间和,然后使用打标记的形式来处理链并的问题。链上的点都打上标记,只有打标记的点的权值才会计入答案,这个线段树写是容易的。记得每次清空这次打过的标记。
至于那个取模,模数是2147483648,我们可以用int自然溢出以后对2147483647取与,这样就能把符号位搞掉,得到取模后的答案,减小常数。
吐槽:不要用memset处理很大的数组,会TLE。线段树做是O(\log n)比memsetO(n)优。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
#include <vector>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 200005;
int n, q, ut, vt, op, xt, kt;
int fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
std::vector<int> gra[MAXN];
inline void addedge(int u, int v) {
gra[u].push_back(v);
gra[v].push_back(u);
}
inline void dfs1(int u) {
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
dfs1(v);
siz[u] += siz[v] + 1;
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
inline void dfs2(int u, int tp) {
top[u] = tp;
dfn[u] = ++cnt;
ptn[dfn[u]] = u;
if(son[u]) dfs2(son[u], tp);
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == son[u] || v == fa[u]) continue;
dfs2(v, v);
}
}
int val[MAXN << 2], sum[MAXN << 2], add[MAXN << 2], tag[MAXN << 2];
inline void pushdown(int o, int l, int r) {
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(add[o]) {
add[lch] += add[o];
add[rch] += add[o];
val[lch] += add[o] * (mid - l + 1);
val[rch] += add[o] * (r - mid);
add[o] = 0;
}
if(tag[o] != -1) {
tag[lch] = tag[rch] = tag[o];
sum[lch] = val[lch] * tag[o];
sum[rch] = val[rch] * tag[o];
tag[o] = -1;
}
}
inline void modify(int o, int l, int r, int ll, int rr, int v) {
if(l >= ll && r <= rr) {
val[o] += v * (r - l + 1);
add[o] += v;
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(ll <= mid) modify(lch, l, mid, ll, rr, v);
if(rr > mid) modify(rch, mid + 1, r, ll, rr, v);
val[o] = val[lch] + val[rch];
}
inline void select(int o, int l, int r, int ll, int rr, int v) {
if(l >= ll && r <= rr) {
sum[o] = val[o] * v;
tag[o] = v;
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(ll <= mid) select(lch, l, mid, ll, rr, v);
if(rr > mid) select(rch, mid + 1, r, ll, rr, v);
sum[o] = sum[lch] + sum[rch];
}
inline void modify(int u, int v, int x) {
int tu = top[u], tv = top[v];
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(u, v);
std::swap(tu, tv);
}
modify(1, 1, n, dfn[tv], dfn[v], x);
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
modify(1, 1, n, dfn[u], dfn[v], x);
}
inline void select(int u, int v) {
int tu = top[u], tv = top[v];
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(u, v);
std::swap(tu, tv);
}
select(1, 1, n, dfn[tv], dfn[v], 1);
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
select(1, 1, n, dfn[u], dfn[v], 1);
}
int main() {
n = readint();
for(int i = 1; i < n; i++) {
ut = readint(); vt = readint();
addedge(ut, vt);
}
dfs1(1);
dfs2(1, 1);
q = readint();
while(q--) {
op = readint();
if(op == 0) {
ut = readint(); xt = readint();
modify(1, 1, n, dfn[ut], dfn[ut] + siz[ut], xt);
} else {
kt = readint();
while(kt--) {
ut = readint(); vt = readint();
select(ut, vt);
}
printf("%d\n", sum[1] & 2147483647);
select(1, 1, n, 1, n, 0);
}
}
return 0;
}