作者: KSkun

[USACO13OPEN]阴和阳Yin and Yang 题解

[USACO13OPEN]阴和阳Yin and Yang 题解

题目地址:洛谷:【P3085】[USACO13OPEN]阴和阳Yin and Yang – 洛谷、BZOJ:Problem 3127. — [Usaco2013 Open]Yin and Yang

题目描述

Farmer John is planning his morning walk on the farm. The farm is structured like a tree: it has N barns (1 <= N <= 100,000) which are connected by N-1 edges such that he can reach any barn from any other. Farmer John wants to choose a path which starts and ends at two different barns, such that he does not traverse any edge twice. He worries that his path might be a little long, so he also wants to choose another “rest stop” barn located on this path (which is distinct from the start or the end).
Along each edge is a herd of cows, either of the Charcolais (white hair) or the Angus (black hair) variety. Being the wise man that he is, Farmer John wants to balance the forces of yin and yang that weigh upon his walk. To do so, he wishes to choose a path such that he will pass by an equal number of Charcolais herds and Angus herds– both on the way from the start to his rest stop, and on the way from the rest stop to the end.
Farmer John is curious how many different paths he can choose that are “balanced” as described above. Two paths are different only if they consist of different sets of edges; a path should be counted only once even if there are multiple valid “rest stop” locations along the path that make it balanced.
Please help determine the number of paths Farmer John can choose!
给出一棵n个点的树,每条边为黑色或白色。问满足以下条件的路径条数:路径上存在一个不是端点的点,使得两端点到该点的路径上两种颜色的边数都相等。

输入输出格式

输入格式:
* Line 1: The integer N.
* Lines 2..N: Three integers a_i, b_i and t_i, representing the two barns that edge i connects. t_i is 0 if the herd along that edge is Charcolais, and 1 if the herd is Angus.

输出格式:
* Line 1: One integer, representing the number of possible paths Farmer John can choose from.

输入输出样例

输入样例#1:

7 
1 2 0 
3 1 1 
2 4 0 
5 2 0 
6 3 1 
5 7 1 

输出样例#1:

1 

说明

There are 7 barns and 6 edges. The edges from 1 to 2, 2 to 4 and 2 to 5 have Charcolais herds along them.
No path of length 2 can have a suitable rest stop on it, so we can only consider paths of length 4. The only path that has a suitable rest stop is 3-1-2-5-7, with a rest stop at 2.

题解

[BZOJ3697]采药人的路径一题。

代码

// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

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;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 200005, INF = 1e9;

struct Edge {
    int to, type, nxt;
} gra[MAXN << 1];
int head[MAXN], tot;

inline void addedge(int u, int v, int type) {
    gra[tot] = Edge {v, type, head[u]}; head[u] = tot++;
    gra[tot] = Edge {u, type, head[v]}; head[v] = tot++;
}

int n;
LL ans;

int siz[MAXN], rt, rtsiz;
bool vis[MAXN];

inline void findrt(int u, int f, int tot) {
    siz[u] = 1; int mxsiz = 0;
    for(int i = head[u]; ~i; i = gra[i].nxt) {
        int v = gra[i].to, t = gra[i].type;
        if(v == f || vis[v]) continue;
        findrt(v, u, tot);
        siz[u] += siz[v];
        mxsiz = std::max(mxsiz, siz[v]);
    }
    mxsiz = std::max(mxsiz, tot - siz[u]);
    if(mxsiz < rtsiz) {
        rt = u; rtsiz = mxsiz;
    }
}

int f[MAXN][2], g[MAXN][2], mxdep, cnt[MAXN];

inline void calsubt(int u, int fa, int d, int dep) {
    mxdep = std::max(mxdep, dep);
    if(cnt[d]) f[d][1]++; else f[d][0]++;
    cnt[d]++;
    for(int i = head[u]; ~i; i = gra[i].nxt) {
        int v = gra[i].to, t = gra[i].type;
        if(v == fa || vis[v]) continue;
        calsubt(v, u, d + gra[i].type, dep + 1);
    }
    cnt[d]--;
}

inline void work(int u) {
    int mx = 0;
    g[n][0] = 1;
    for(int i = head[u]; ~i; i = gra[i].nxt) {
        int v = gra[i].to, t = gra[i].type;
        if(vis[v]) continue;
        mxdep = 1; calsubt(v, u, n + t, 1);
        mx = std::max(mx, mxdep);
        ans += 1ll * (g[n][0] - 1) * f[n][0];
        for(int j = -mxdep; j <= mxdep; j++) {
            ans += 1ll * g[n - j][1] * f[n + j][1] + 1ll * g[n - j][1] * f[n + j][0] 
                + 1ll * g[n - j][0] * f[n + j][1];
        }
        for(int j = -mxdep; j <= mxdep; j++) {
            g[n + j][0] += f[n + j][0];
            g[n + j][1] += f[n + j][1];
            f[n + j][0] = f[n + j][1] = 0;
        }
    }
    for(int i = -mx; i <= mx; i++) {
        g[n + i][0] = g[n + i][1] = 0;
    }
}

inline void divide(int u) {
    vis[u] = true;
    work(u);
    for(int i = head[u]; ~i; i = gra[i].nxt) {
        int v = gra[i].to, t = gra[i].type;
        if(vis[v]) continue;
        rt = 0; rtsiz = INF; findrt(v, u, siz[v]);
        divide(rt);
    }
}

int u, v, t;

int main() {
    memset(head, -1, sizeof(head));
    n = readint();
    for(int i = 1; i < n; i++) {
        u = readint(); v = readint(); t = readint();
        addedge(u, v, t ? t : -1);
    }
    rt = -1; rtsiz = INF; findrt(1, 0, n);
    divide(rt);
    printf("%lld", ans);
    return 0;
}
[SCOI2009]windy数 题解

[SCOI2009]windy数 题解

题目地址:洛谷:【P2657】[SCOI2009]windy数 – 洛谷、BZOJ:Problem 1026. — [SCOI2009]windy数

题目描述

windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,在A和B之间,包括A和B,总共有多少个windy数?

输入输出格式

输入格式:
包含两个整数,A B。

输出格式:
一个整数

输入输出样例

输入样例#1:

1 10

输出样例#1:

9

输入样例#2:

25 50

输出样例#2:

20

说明

100%的数据,满足 1 <= A <= B <= 2000000000 。

题解

很简单的数位DP,直接套路用上去,判一下差是否不小于2即可。

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

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; register char c = fgc();
    for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
    for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
    return res * neg;
}

const int MAXN = 50;

LL a, b, num[MAXN], dp[MAXN][MAXN][2][2];

inline LL dfs(int step, int lst, bool zero, bool lim) {
    if(!step) return !zero;
    if(dp[step][lst][zero][lim] != -1) return dp[step][lst][zero][lim];
    int limm = lim ? num[step] : 9, res = 0;
    for(int i = 0; i <= limm; i++) {
        if(abs(lst - i) < 2 && !zero) continue;
        res += dfs(step - 1, i, zero && !i, lim && i == num[step]);
    }
    return dp[step][lst][zero][lim] = res;
}

inline LL cal(LL x) {
    int n = 0;
    while(x) {
        num[++n] = x % 10; x /= 10;
    }
    LL res = 0;
    for(int i = 0; i <= num[n]; i++) {
        res += dfs(n - 1, i, i == 0, i == num[n]);
    }
    return res;
}

int main() {
    memset(dp, -1, sizeof(dp));
    a = readint(); b = readint();
    printf("%lld", cal(b) - cal(a - 1));
    return 0;
}
[BZOJ3038]上帝造题的七分钟2 题解

[BZOJ3038]上帝造题的七分钟2 题解

题目地址:洛谷:【P4145】上帝造题的七分钟2 / 花神游历各国 – 洛谷、BZOJ:Problem 3038. — 上帝造题的七分钟2

题目描述

XLk觉得《上帝造题的七分钟》不太过瘾,于是有了第二部。
“第一分钟,X说,要有数列,于是便给定了一个正整数数列。
第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。
第三分钟,k说,要能查询,于是便有了求一段数的和的操作。
第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。
第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。
第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。”
——《上帝造题的七分钟·第二部》
所以这个神圣的任务就交给你了。

题意简述

给你一个序列,两种操作:

  1. 区间每个数开方
  2. 区间求和

输入输出格式

输入格式:
第一行一个整数n,代表数列中数的个数。
第二行n个正整数,表示初始状态下数列中的数。
第三行一个整数m,表示有m次操作。
接下来m行每行三个整数k,l,r,k=0表示给[l,r]中的每个数开平方(下取整),k=1表示询问[l,r]中各个数的和。

输出格式:
对于询问操作,每行输出一个回答。

输入输出样例

输入样例#1:

10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8

输出样例#1:

19
7
6

说明

1:对于100%的数据,1<=n<=100000,1<=l<=r<=n,数列中的数大于0,且不超过1e12。
2:数据不保证L<=R 若L>R,请自行交换L,R,谢谢!

题解

考虑线段树做,但是发现开放操作没法打标记,因此只能一路递归到底。
然而这样会TLE,因此我们要优化。我们发现每次开方都会对指数数量级减半,没几次开方数就会变为1,1开方仍是1,因此对于那些区间内不含大于1的区间,我们没必要继续递归对它们开方了,节省了不少的时间。

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cmath>

#include <algorithm>

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; register char c = fgc();
    for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
    for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
    return res * neg;
}

const int MAXN = 100005;

int n, m;

LL a[MAXN], sum[MAXN << 2], mx[MAXN << 2];

#define lch o << 1
#define rch o << 1 | 1
#define mid ((l + r) >> 1)

inline void build(int o, int l, int r) {
    if(l == r) {
        mx[o] = sum[o] = a[l]; return;
    }
    build(lch, l, mid);
    build(rch, mid + 1, r);
    sum[o] = sum[lch] + sum[rch];
    mx[o] = std::max(mx[lch], mx[rch]);
}

inline void modify(int o, int l, int r, int ll, int rr) {
    if(l == r) {
        mx[o] = sum[o] = sqrt(sum[o]); return;
    }
    if(ll <= mid && mx[lch] > 1) modify(lch, l, mid, ll, rr);
    if(rr > mid && mx[rch] > 1) modify(rch, mid + 1, r, ll, rr);
    sum[o] = sum[lch] + sum[rch];
    mx[o] = std::max(mx[lch], mx[rch]);
}

inline LL query(int o, int l, int r, int ll, int rr) {
    if(l >= ll && r <= rr) return sum[o];
    LL res = 0;
    if(ll <= mid) res += query(lch, l, mid, ll, rr);
    if(rr > mid) res += query(rch, mid + 1, r, ll, rr);
    return res;
}

int op, l, r;

int main() {
    n = readint(); 
    for(int i = 1; i <= n; i++) {
        a[i] = readint();
    }
    build(1, 1, n);
    m = readint();
    while(m--) {
        op = readint(); l = readint(); r = readint();
        if(l > r) std::swap(l, r);
        if(op == 0) {
            modify(1, 1, n, l, r);
        } else {
            printf("%lld\n", query(1, 1, n, l, r));
        }
    }
    return 0;
}