标签: DFS

[APIO2014]连珠线 题解

[APIO2014]连珠线 题解

题目地址:洛谷:【P3647】[APIO2014]连珠线 – 洛谷、BZOJ: 

[WC2010]重建计划 题解

[WC2010]重建计划 题解

题目地址:洛谷:【P4292】[WC2010]重建计划 – 洛谷、BZOJ:P 

[BZOJ3208]花神的秒题计划Ⅰ 题解

[BZOJ3208]花神的秒题计划Ⅰ 题解

题目地址:BZOJ:Problem 3208. — 花神的秒题计划Ⅰ

题目描述

背景【backboard】:
Memphis等一群蒟蒻出题中,花神凑过来秒题……
描述【discribe】:
花花山峰峦起伏,峰顶常年被雪,Memphis打算帮花花山风景区的人员开发一个滑雪项目。
我们可以把风景区看作一个n*n的地图,每个点有它的初始高度,滑雪只能从高处往低处滑【严格大于】。但是由于地势经常变动【比如雪崩、滑坡】,高度经常变化;同时,政府政策规定对于每个区域都要间歇地进行保护,防止环境破坏。现在,滑雪项目的要求是给出每个n*n个点的初始高度,并给出m个命令,C a b c表示坐标为a,b的点的高度改为c;S a b c d表示左上角为a,b右下角为c,d的矩形地区开始进行保护,即不能继续滑雪;B a b c d表示左上角为a b,右下角为c d的矩形地区取消保护,即可以开始滑雪;Q表示询问现在该风景区可以滑雪的最长路径为多少。对于每个Q要作一次回答。
花神一看,这不是超简单!立刻秒出了标算~

题意简述

动态改变点权和点的可用性的滑雪DP。

输入输出格式

输入格式:
第一行n,第二行开始n*n的地图,意义如上;接下来一个m,然后是m个命令,如上

输出格式:
对于每一个Q输出单独一行的回答

输入输出样例

输入样例#1:

5
1 2 3 4 5
10 9 8 7 6
11 12 13 14 15
20 19 18 17 16
21 22 23 24 25
5
C 1 1 3
Q
S 1 3 5 5
S 3 1 5 5
Q

输出样例#1:

24
3

说明

样例解释:
第一个Q路线为:25->24->23->22….->3->2
第二个Q的路线为:10->9->2
100%的数据:1<=n<=700;1<=m<=1000000;其中Q、S、B操作总和<=100;
题中所有数据不超过2*10^9

题解

动态滑雪。
考虑到QSB操作操作很少,我们是可以接受O(n^3)的复杂度的,因此暴力改,每次全图重新跑记忆化搜索即可。

代码

// 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;
}

inline bool isop(char c) {
    return c == 'C' || c == 'S' || c == 'B' || c == 'Q';
}

inline char readop() {
    char c;
    while(!isop(c = fgc())) {}
    return c;
}

const int MAXN = 705;
const int fix[2][4] = {{1, -1, 0, 0}, {0, 0, 1, -1}};

int n, m, mmp[MAXN][MAXN], dp[MAXN][MAXN];
bool prot[MAXN][MAXN];

inline int dfs(int x, int y) {
    if(prot[x][y]) return -1e9;
    if(dp[x][y] != -1) return dp[x][y];
    dp[x][y] = 1;
    for(int i = 0; i < 4; i++) {
        int nx = x + fix[0][i], ny = y + fix[1][i];
        if(nx < 1 || nx > n || ny < 1 || ny > n) continue;
        if(mmp[x][y] > mmp[nx][ny]) dp[x][y] = std::max(dp[x][y], dfs(nx, ny) + 1);
    }
    return dp[x][y];
}

char op;
int a, b, c, d;

int main() {
    n = readint();
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            mmp[i][j] = readint();
        }
    }
    m = readint();
    while(m--) {
        op = readop();
        if(op == 'Q') {
            int mx = 0;
            memset(dp, -1, sizeof(dp));
            for(int i = 1; i <= n; i++) {
                for(int j = 1; j <= n; j++) {
                    mx = std::max(mx, dfs(i, j));
                }
            }
            printf("%d\n", mx);
        } else if(op == 'C') {
            a = readint(); b = readint(); c = readint();
            mmp[a][b] = c;
        } else if(op == 'S') {
            a = readint(); b = readint(); c = readint(); d = readint();
            for(int i = a; i <= c; i++) {
                for(int j = b; j <= d; j++) {
                    prot[i][j] = true;
                }
            }
        } else {
            a = readint(); b = readint(); c = readint(); d = readint();
            for(int i = a; i <= c; i++) {
                for(int j = b; j <= d; j++) {
                    prot[i][j] = false;
                }
            }
        }
    }
    return 0;
}
[BZOJ3551][ONTAK2010]Peaks加强版 题解

[BZOJ3551][ONTAK2010]Peaks加强版 题解

题目地址:BZOJ:Problem 3551. — [ONTAK2010]Pe 

[POI2003]Monkeys 题解

[POI2003]Monkeys 题解

题目地址:BZOJ:Problem 2610. — [Poi2003]Monk 

[BZOJ3439]Kpm的MC密码 题解

[BZOJ3439]Kpm的MC密码 题解

题目地址:BZOJ:Problem 3439. — Kpm的MC密码

题目描述

Kpm当年设下的问题是这样的:
现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串。
系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分别为k1…kn,对于每一个ki,要求你求出列出的n个字符串中所有是si的kpm串的字符串的编号中第ki小的数,如果不存在第ki小的数,则用-1代替。(比如说给出的字符串是cd,abcd,bcd,此时k1=2,那么”cd”的kpm串有”cd”,”abcd”,”bcd”,编号分别为1,2,3其中第2小的编号就是2)(PS:如果你能在相当快的时间里回答完所有n个ki的查询,那么你就可以成功帮kpm进入MC啦~~)

题意简述

给n个字符串,按1~n编号。对于每一个字符串s,询问s是后缀的字符串中,编号第k大的编号。

输入输出格式

输入格式:
第一行一个整数 n 表示字符串的数目
接下来第二行到n+1行总共n行,每行包括一个字符串,第i+1行的字符串表示编号为i的字符串
接下来包括n行,每行包括一个整数ki,意义如上题所示

输出格式:
包括n行,第i行包括一个整数,表示所有是si的kpm串的字符串的编号中第ki小的数

输入输出样例

输入样例#1:

3
cd
abcd
bcd
2
3
1

输出样例#1:

2
-1
2

说明

样例解释
“cd”的kpm 串有”cd”,”abcd”,”bcd”,编号为1,2,3,第2小的编号是2,”abcd”的kpm串只有一个,所以第3小的编号不存在,”bcd”的kpm串有”abcd”,”bcd”,第1小的编号就是2。
数据范围与约定
对于100%的数据,1<=n<=100000

题解

要查后缀是某串的字符串有哪些,自然可以想到反着插入Trie树,找该串末结点对应的子树中的结尾标记。既然是子树查询k大,我们可以按DFS序建主席树来查。
复杂度O(n \log n)

代码

// 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 = 100005;

int ch[MAXN][26], ed[MAXN], tot;
int head[MAXN], val[MAXN], nxt[MAXN], ltot;

inline void insert(char *str, int id) {
    int n = strlen(str + 1), p = 0;
    for(int i = n; i >= 1; i--) {
        int t = str[i] - 'a';
        if(!ch[p][t]) ch[p][t] = ++tot;
        p = ch[p][t];
    }
    val[++ltot] = id; nxt[ltot] = head[p]; head[p] = ltot; ed[id] = p;
}

int dfn[MAXN], vn[MAXN], siz[MAXN], clk;

inline void dfs(int u) {
    dfn[u] = ++clk; vn[dfn[u]] = u;
    siz[u] = 1;
    for(int i = 0; i < 26; i++) {
        if(ch[u][i]) {
            dfs(ch[u][i]); siz[u] += siz[ch[u][i]];
        }
    }
}

struct Node {
    int lch, rch, val, id;
} tr[MAXN << 4];
int rt[MAXN], stot;

inline void insert(int &o, int l, int r, int x, int id) {
    if(tr[o].id != id) {
        tr[++stot] = tr[o]; o = stot; tr[o].id = id;
    }
    tr[o].val++;
    if(l == r) return;
    int mid = (l + r) >> 1;
    if(x <= mid) insert(tr[o].lch, l, mid, x, id);
    else insert(tr[o].rch, mid + 1, r, x, id);
}

inline int query(int o1, int o2, int l, int r, int k) {
    if(l == r) return l;
    int mid = (l + r) >> 1, lsiz = tr[tr[o2].lch].val - tr[tr[o1].lch].val;
    if(k <= lsiz) return query(tr[o1].lch, tr[o2].lch, l, mid, k);
    else return query(tr[o1].rch, tr[o2].rch, mid + 1, r, k - lsiz);
}

int n, k;
char s[MAXN];

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%s", s + 1);
        insert(s, i);
    }
    dfs(0);
    for(int i = 1; i <= clk; i++) {
        rt[i] = rt[i - 1];
        for(int j = head[vn[i]]; j; j = nxt[j]) {
            insert(rt[i], 1, n, val[j], i);
        }
    }
    for(int i = 1; i <= n; i++) {
        scanf("%d", &k);
        if(tr[rt[dfn[ed[i]] + siz[ed[i]] - 1]].val - tr[rt[dfn[ed[i]] - 1]].val < k) {
            puts("-1"); continue;
        }
        printf("%d\n", query(rt[dfn[ed[i]] - 1], rt[dfn[ed[i]] + siz[ed[i]] - 1], 1, n, k));
    }
    return 0;
}
[POI2014]HOT-Hotels 题解

[POI2014]HOT-Hotels 题解

题目地址:洛谷:【P3565】[POI2014]HOT-Hotels – 洛谷 

[洛谷2664]树上游戏 题解

[洛谷2664]树上游戏 题解

题目地址:洛谷:【P2664】树上游戏 – 洛谷 题目描述 lrb有一棵树,树 

[SCOI2016]幸运数字 题解

[SCOI2016]幸运数字 题解

题目地址:洛谷:【P3292】[SCOI2016]幸运数字 – 洛谷、BZOJ:Problem 4568. — [Scoi2016]幸运数字

题目描述

A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。
一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。
例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9(5 xor 7 xor 11)。
有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5 和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。

题意简述

给一棵树,每个点有权值,求两点间路径上所有点的权值取若干个求异或和的最大值。

输入输出格式

输入格式:
第一行包含 2 个正整数 n ,q,分别表示城市的数量和旅行者数量。
第二行包含 n 个非负整数,其中第 i 个整数 Gi 表示 i 号城市的幸运值。
随后 n-1 行,每行包含两个正整数 x ,y,表示 x 号城市和 y 号城市之间有一条道路相连。
随后 q 行,每行包含两个正整数 x ,y,表示这名旅行者的旅行计划是从 x 号城市到 y 号城市。N<=20000,Q<=200000,Gi<=2^60

输出格式:
输出需要包含 q 行,每行包含 1 个非负整数,表示这名旅行者可以保留的最大幸运值。

输入输出样例

输入样例#1:

4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4

输出样例#1:

14 
11

题解

把线性基搞树上去了,我们自然要在树上维护线性基。我们考虑使用倍增法求LCA,并把一个点到它所有倍增父亲这一段路径的线性基预处理出来,在倍增上跳的过程中就是O(\log n)次合并线性基的过程。
倍增LCAO(\log n),维护线性基O(\log n),合并线性基O(\log^2 n),因此总复杂度O(n \log^3 n)

代码

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

#include <algorithm>
#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;
    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 = 20005;

std::vector<int> gra[MAXN];

struct LinearBasis {
    LL mat[65];
    LinearBasis() {
        memset(mat, 0, sizeof(mat));
    }
    inline void insert(LL x) {
        for(int i = 60; i >= 0; i--) {
            if(x & (1ll << i)) {
                if(!mat[i]) {
                    mat[i] = x; break;
                } else {
                    x ^= mat[i];
                }
            }
        }
    }
    inline void merge(LinearBasis &x) {
        for(int i = 60; i >= 0; i--) {
            if(x.mat[i]) insert(x.mat[i]);
        }
    }
    inline LL max() {
        LL res = 0;
        for(int i = 60; i >= 0; i--) {
            res = std::max(res, res ^ mat[i]);
        }
        return res;
    }
} lb[MAXN][20];

int anc[MAXN][20], dep[MAXN];
LL w[MAXN];

inline void dfs(int u) {
    for(int i = 1; i < 15; i++) {
        anc[u][i] = anc[anc[u][i - 1]][i - 1];
        lb[u][i].merge(lb[u][i - 1]);
        lb[u][i].merge(lb[anc[u][i - 1]][i - 1]);
    }
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(v == anc[u][0]) continue;
        anc[v][0] = u;
        dep[v] = dep[u] + 1;
        lb[v][0].insert(w[u]);
        dfs(v);
    }
}

inline LL query(int x, int y) {
    LinearBasis res;
    if(x == y) {
        res.insert(w[x]); return res.max();
    }
    if(dep[x] > dep[y]) std::swap(x, y);
    int del = dep[y] - dep[x];
    for(int i = 14; i >= 0; i--) {
        if(del & (1 << i)) {
            res.merge(lb[y][i]);
            y = anc[y][i];
        }
    }
    if(x == y) return res.max();
    for(int i = 14; i >= 0; i--) {
        if(anc[x][i] != anc[y][i]) {
            res.merge(lb[x][i]);
            res.merge(lb[y][i]);
            x = anc[x][i];
            y = anc[y][i];
        }
    }
    res.merge(lb[x][0]);
    res.insert(w[y]);
    return res.max();
}

int n, q, x, y;

int main() {
    n = readint(); q = readint();
    for(int i = 1; i <= n; i++) {
        w[i] = readint();
        lb[i][0].insert(w[i]);
    }
    for(int i = 1; i < n; i++) {
        x = readint(); y = readint();
        gra[x].push_back(y);
        gra[y].push_back(x);
    }
    dfs(1);
    while(q--) {
        x = readint(); y = readint();
        printf("%lld\n", query(x, y));
    }
    return 0;
}
[ZJOI2010]数字计数 题解

[ZJOI2010]数字计数 题解

题目地址:洛谷:【P2602】[ZJOI2010]数字计数 – 洛谷、BZOJ