Codeforces Round #294 (Div. 2)

D. A and B and Interesting Substrings

题意:给定26个小写字母的权值和一个字符串, 求出该字符串有多少个连续的子串,首尾相同, 并且除了首尾权值和为0。
思路:权值和为0说明子串的两个端点的前缀和相同, 先预处理出前缀和, 然后因为首尾要相同, 所以开26个map记录一个某个字符的前缀和的出现次数。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <cstdlib>
using namespace std;

#define LL long long
#define inf 0x3f3f3f3f
#define eps 1e-8
#define ULL unsigned long long
#define mxn 100020
#define mxe 200020
#define mxnode 20020
#define mod 1000000007
#define vi vector<int>
#define pii pair<int, int>
#define vii vector<pii >


LL sum[mxn];
int a[30];
char s[mxn];
map<LL, int> b[30];
int main() {
    while(1) {
        for(int i = 0; i < 26; ++i) scanf("%d", &a[i]);
        scanf("%s", s);
        int n = strlen(s);
        for(int i = 0; s[i]; ++i)
            sum[i] = i == 0? a[s[i]-'a']: sum[i-1] + a[s[i]-'a'];
        LL ans = 0;
        for(int i = 0; i < 30; ++i) b[i].clear();
        for(int i = 0; s[i]; ++i) {
            int c = s[i] - 'a';
            ans += b[c][sum[i-1]];
            ++b[c][sum[i]];
        }
        printf("%I64d\n", ans);
        return 0;
    }
    return 0;
}

E.  A and B and Lecture Rooms

题意:给定一棵树,和m个询问, 每个询问包括两个节点u和v,要求求出树上跟到u和v距离相同的节点的个数。
思路: 先求出u到v的距离dis, 可以用lca求, 如果dis==0的话, 答案就是n, 如果dis为奇数的话, 答案就是0.
最后,如果dis是偶数, 假设u的深度小于v的深度, 则求出v的dis/2级祖先w, 如果w和p相同的话, v的dis/2-1级祖先是x, u的dis/2-1级祖先是y, 答案就是n-sz[x]-sz[y],
如果w和p不相同的话, 答案就是sz[w] - sz[x].
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <cstdlib>
using namespace std;

#define LL long long
#define inf 0x3f3f3f3f
#define eps 1e-8
#define ULL unsigned long long
#define mxn 100020
#define mxe 200020
#define mxnode 20020
#define mod 1000000007
#define vi vector<int>
#define pii pair<int, int>
#define vii vector<pii >


int fst[mxn], nxt[mxe], to[mxe], e;
void init() {
    memset(fst, -1, sizeof fst);
    e = 0;
}
void add(int u, int v) {
    to[e] = v, nxt[e] = fst[u], fst[u] = e++;
}
int n, m;
int sz[mxn];
int fa[22][mxn], dep[mxn];
void dfs(int u, int p, int d) {
    sz[u] = 1;
    dep[u] = d;
    fa[0][u] = p;
    for(int i = fst[u]; ~i; i = nxt[i]) {
        int v = to[i];
        if(v == p) continue;
        dfs(v, u, d + 1);
        sz[u] += sz[v];
    }
}

void lcaInit() {
    for(int k = 0; k < 21; ++k) {
        for(int i = 1; i <= n; ++i) {
            if(fa[k][i] != -1)
                fa[k+1][i] = fa[k][fa[k][i]];
            else
                fa[k+1][i] = -1;
        }
    }
}
int lca(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    for(int k = 0; k < 22; ++k) {
        if((dep[v] - dep[u]) >> k & 1)
            v = fa[k][v];
    }
    if(u == v) return u;
    for(int k = 21; k >= 0; --k) {
        if(fa[k][u] != fa[k][v])
            u = fa[k][u], v = fa[k][v];
    }
    return fa[0][u];
}
int kfa(int u, int k) {
    int t = u;
    for(int i = 0; i < 22; ++i) {
        if(k >> i & 1)
            u = fa[i][u];
    }
    return u;
}
int main() {
//  freopen("tt.txt", "r", stdin);
    while(scanf("%d", &n) != EOF) {
        init();
        for(int i = 1; i < n; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            add(u, v);
            add(v, u);
        }
        dfs(1, -1, 0);
        lcaInit();
        scanf("%d", &m);
        while(m--) {
            int u, v;
            scanf("%d%d", &u, &v);
            int p = lca(u, v);
            int dis = dep[u] + dep[v] - 2 * dep[p];
            if(dis == 0) {
                printf("%d\n", n);
                continue;
            }
            if(dis % 2 == 1) {
                puts("0");
                continue;
            }
            if(dep[u] > dep[v]) swap(u, v);
            int w = kfa(v, dis / 2);
            int x = kfa(v, dis / 2 - 1);
            int ans = 0;
            if(w == p) {
                int y = kfa(u, dis / 2 - 1);
                ans = n - sz[x] - sz[y];
            }
            else {
                ans = sz[w] - sz[x];
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值