元宵爆零赛

闫神的元宵欢乐爆零赛,全场2/3爆零啦...

好吧其实是我太菜,才讲的原题都不会。


 

T1 树上游走(tree

【题目描述】

有一棵n个点的树。

有m次询问,每次给定起点s和终点t,会从s走到t。由于眼神不好,它会按如下方式走路:

(1)初始时在s,如果到达t就立刻停止;

(2)如果相邻点中存在离s更远且没走过的点,那么等概率随机一个满足条件的点走过去;

(3)否则,往s所在方向走一步。

你需要求出期望经过多少个点(包括s和t)。一个点被多次经过只计算一次。

【输入数据】

第一行两个整数n,m,接下来n-1行每行两个整数u,v表示一条边,接下来m行每行两个整数s,t表示一组询问。

【输出数据】

每组询问输出一行一个实数表示答案,保留一位小数。

【样例输入】

3 2

1 2

2 3

1 2

1 3

2 3

【样例输出】

2.0

3.0

2.5

【数据范围】

对于10%的数据,n,m<=7。

对于30%的数据,n,m<=100。

对于60%的数据,n<=5000。

对于100%的数据,1<=n,m<=500000,s!=t。

【题解思路】

推一下样例会发现样例第三个询问好像跟我们预想的不大一样,预想0.5*(1+1)+1*(1+0.5*(1+1))=3,实际0.5*(1+1+1)+0.5*(1+1)=2.5;诶这个0.5是乘在外面的诶...

这是一颗树,那么从s到t的最短路径是唯一的,无论怎么走,都要走遍s到t的树上最短路径,假若在任何时刻走到了s到t的最短路的节点上,它会随机一个点走下去,等价于求儿子的全排列哇...

假如此时走到了x,选择了一个不是s到t最短路径上的节点,然后遍历其整个子树之后必然要回去原来的x节点,以此往返,因此就相当于找一个儿子排列来走。从s到t的路径上的点与其等深度的点有同等几率被走到,当走到了s到t路径上点时停止遍历子树而又返回的操作。这个排列以一个在s到t路径上的点为开始,以另一个在s到t的路径上的点结束,对于不在s到t路径上的点,都有1/2的几率(等概率随机选择,有或无)出现在这个排列之中。

【code】

#include<bits/stdc++.h>
using namespace std;
int read(){
    int x = 0, w = 0;
    char ch = 0;
    while (!isdigit(ch)) {w |= ch == '-'; ch = getchar();}
    while (isdigit(ch)) {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
    return w ? -x : x;
}
int a[500010], n, m, deep[500010], f[21][500010];
vector<int> v[500010];
int p;
void dfs(int x){
    for (int i = 1; i <= 20; ++i)
        f[i][x] = f[i - 1][f[i - 1][x]];
    a[x] = 1;
    for (int i = 0; i < v[x].size(); ++i)
        if (v[x][i] != f[0][x])
        {
            f[0][v[x][i]] = x;
            deep[v[x][i]] = deep[x] + 1;
            dfs(v[x][i]);
            a[x] += a[v[x][i]];
        }
}
int LCA(int x, int y){
    if (deep[x] < deep[y]) swap(x, y);
    if (deep[x] > deep[y]){
        for (int i = 20; ~i; --i)
            if (deep[x] - (1 << i) > deep[y]) x = f[i][x];
        if (f[0][x] == y){
            p = x;
            return y;
        }
        x = f[0][x];
    }
    for (int i = 20; ~i; --i)
        if (f[i][x] != f[i][y]) x = f[i][x], y = f[i][y];
    return f[0][x];
}
int main(int argc, char const *argv[])
{
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    n = read(), m = read();
    for (int i = 1; i < n; ++i)
    {
        int x = read(), y = read();
        v[x].push_back(y);
        v[y].push_back(x);
    }
    deep[1] = 1;
    dfs(1);
    while (m--)
    {
        int s = read(), t = read(), g, k;
        int lca = LCA(s, t);
        if (lca == t) k = n - a[p] - 1;
        else k = a[t] - 1;
        g = n + (deep[s] + deep[t] - 2 * deep[lca] + 1) - k;
        printf("%d.%d\n", g / 2, g % 2 * 5);
    }
    return 0;
}
code from zs

 

 

T2最小质因数(prime

【题目描述】

精通数数,它想让你帮他算出[1,n]中所有合数的最小质因数的k次方和。

【输入数据】

一行两个整数n,k。

【输出数据】

输出一行一个整数表示答案。对264取模。

【样例输入1

10 1

【样例输出1

11

【样例输入2

10 2

【样例输出2

25

【数据范围】

对于20%的数据,n<=108

对于60%的数据,n<=1010

对于100%的数据,1<=n<=2*1011,1<=k<=109

数据有梯度。

【题解思路】

20分:看似很送分的线性筛,你要试看看开3个1e8的数组吗...

60分?:对于x<=k的情况,进行常见的枚举子集容斥;对于x>k的情况,n/x较小,在n/k的范围内进行线性筛/埃氏筛法。(如何确定这个k值,优秀一点的可能会有意想不到的收获)

100分:进行子集容斥时,枚举子集后贡献形如(-1)i(n/S),而n/S只有O(sqrt(n))种取值,对这个进行记忆化即可。

【code】传送门

 

T3路径(path

【题目描述】

有一张n个点m条边的无向连通图,它想知道有多少条长度不小于k的简单路径。

【输入数据】

第一行三个整数n,m,k,接下来m行每行两个整数表示一条边。

【输出数据】

输出一行一个整数表示答案。

【样例输入】

5 5 2

1 3

2 4

3 5

4 1

5 2

【样例输出】

20

【数据范围】

对于20%的数据,n<=2000。

对于另外50%的数据,m=n-1。

对于100%的数据,1<=k<=n<=100000,n-1<=m<=n。

【题解思路】本题同【BZOJ3648】寝室管理

【code】上面的一行变成超链接的时候,就有代码可以看了

    (总而言之就是ve又挖坑了。。。

posted @ 2019-02-20 16:33  ve-2021  阅读(118)  评论(0编辑  收藏  举报