比赛-thh学长的训练赛 (Aug 16, 2018)

1.) 欧拉回路
打表找规律,打了斯特林,打了组合数,找不出,弃疗。我太蠢了,这题和前面这些东西没什么关系啊。考虑 \(n\) 个点时的完全图 \(n \cdot (n - 1) / 2\) 条边,每条边选与不选两种决策,所以可以得到神奇的数字 \(2^{n(n-1)/2}\) ,而答案就是 \(2^{(n-1)(n-2)/2}\) 。具体证的话,对于 \(n\) 个点的图,拿出一个点,把 \(n-1\) 个点胡乱连边,方案数 \(2^{(n-1)(n-2)/2}\) 。然后让单独拿出来的点与其他点连边。如果某点度数是奇度就把单独拿出来的点与这个点连一条边,否则不连。这样的话,连边方案是唯一的。可以得到:A.) 除了单独拿出来的点外,其他点的度数在连边后为偶数。同时:B.) 根据无向图性质,所有点度数之和为偶数。由 A 和 B 可以得到,单独拿出来的点的度数也为偶数。因为每个点度数都是偶数,所以这样构造的 \(n\) 个点的图含有欧拉回路。并且这样显然会不遗漏地讨论完所有方案。呼……

#include <cstdio>

typedef long long ll;

const ll MOD = 998244353ll;

ll mul(ll a, ll b)
{
    ll t = 0;
    while (b) {
        if (b & 1)
            t = (t + a) % MOD;
        b >>= 1;
        a = (a << 1) % MOD;
    }
    return t;
}

ll mont(ll a, ll b)
{
    ll t = 1;
    a %= MOD;
    while (b) {
        if (b & 1)
            t = mul(t, a);
        b >>= 1;
        a = mul(a, a);
    }
    return t;
}

int main()
{
    ll n;
    scanf("%lld", &n);
    if (n == 1) {
        printf("1\n");
        return 0;
    }
    ll ans;
    if (n & 1)
        ans = mont(mont(2, (n-1)/2), n-2);
    else
        ans = mont(mont(2, (n-2)/2), n-1);
    printf("%lld\n", ans);
    return 0;
}

2.) 乌鸦坐飞机
ljh 大佬考场上想的线段树优化建图,多么窒息的操作……我非常幸运地一眼看出考倍增,然后玄学写错爆 0 ,考完重写了一次就 A 了,暂时不知道错哪。
推一下 dp 方程 \(f_i = min\{f_j + w_i\}\) ,要求 \(j\) 在 \(i\) 到根的路径上。发现是树上极值查询,所以就是倍增啊。

#include <stdio.h>
#include <stack>
#include <cstring>
#include <vector>
#include <cmath>
#include <ctype.h>

using namespace std;

typedef long long ll;

const int _N = 320000;
const int _lgN = 21;

vector<int> G[_N];
int dad[_N][_lgN], D[_N], W[_N], depth[_N], LG[_N], N, lgN;
ll f[_N][_lgN], ans[_N];

int moveup(int p, int s)
{
    for (int i = 0; i <= lgN; ++i)
        if ((s >> i) & 1) p = dad[p][i];
    return p;
}

void dfs(int p, int d)
{
    if (p != 1) {
        depth[p] = depth[d] + 1;
        dad[p][0] = d;
        f[p][0] = ans[d];
        for (int i = 1; i <= lgN; ++i) {
            dad[p][i] = dad[dad[p][i-1]][i-1];
            f[p][i] = min(f[p][i-1], f[dad[p][i-1]][i-1]);
        }
        int x = min(D[p], depth[p]);
        int tmp = moveup(p, x-(1<<LG[x]));

        ans[p] = min(f[tmp][LG[x]], f[p][LG[x]]) + W[p];

    }

    for (int i = G[p].size()-1; i >= 0; --i) {
        int v = G[p][i];
        if (v != d) dfs(v, p);
    }
    return;
}

int main()
{
    scanf("%d", &N);
    for (int x, y, i = 1; i < N; ++i) {
        scanf("%d%d", &x, &y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    for (int i = 1; i <= N; ++i)
        scanf("%d%d", &W[i], &D[i]);
    lgN = log2(N + 0.5) + 1;
    LG[0] = -1;
    for (int i = 1; i <= N; ++i)
        LG[i] = LG[i >> 1] + 1;
    dfs(1, 0);
    int T;
    scanf("%d", &T);
    while (T--) {
        int x;
        scanf("%d", &x);
        printf("%lld\n", ans[x]);
    }
    return 0;
}

3.) 四边形上树
真是妙啊。先考虑暴力,存在四边形的充要条件是任意三边和大于第四边。枚举四边形的最长边 \(d\) ,只要有 \(a+b+c > d\) 就可以了。直接把两点路径上的点排序,然后贪心地找到比枚举的 \(d\) 小的,最大的三个数,判断是否可以作为边长构成四边形。然后就是很精妙的操作了。推理可得,要构造出连续很多个数,还要使他们总是满足 \(a+b+c \leq d\) 的话,当这些数的个数为 40 时,最大的数已经超过 int 上限了。所以对于路径长度 \(>40\) 的询问直接输出 \(Yes\) ,否则暴力排序并判断就好了。

#include <stdio.h>
#include <vector>
#include <algorithm>

const int _N = 120000;
const int X = 40;

using namespace std;

#define SC(a, b) (static_cast<a>(b))

typedef long long ll;

vector<int> G[_N];
int dad[_N], A[_N], B[_N], Bcnt, N;
bool mk[_N];

void dfs(int p)
{
    for (int i = G[p].size()-1; i >= 0; --i) {
        int v = G[p][i];
        if (v != dad[p]) {
            dad[v] = p;
            dfs(v);
        }
    }
    return;
}

int main()
{
    int T;
    scanf("%d", &N);
    for (int i = 1; i <= N; ++i)
        scanf("%d", &A[i]);
    for (int x, y, i = 1; i < N; ++i) {
        scanf("%d%d", &x, &y);
        G[x].push_back(y), G[y].push_back(x);
    }
    scanf("%d", &T);
    dfs(1);
    while (T--) {
        int ins, x, y;
        scanf("%d%d%d", &ins, &x, &y);
        if (ins == 1) {
            A[x] = y;
        } else {
            int a, i;
            for (a = x, i = 1; i <= X; ++i)
                mk[a] = 1, a = dad[a];
            for (a = y, i = 1; i <= X && !mk[a]; ++i)
                a = dad[a];
            if (!mk[a] || !a) {
                printf("Yes\n");
                for (a = x, i = 1; i <= X; ++i)
                    mk[a] = 0, a = dad[a];
                continue;
            }
            int lca = a;
            Bcnt = 0;
            for (a = x; a != lca; a = dad[a])
                B[++Bcnt] = A[a];
            for (a = y; a != lca; a = dad[a])
                B[++Bcnt] = A[a];
            B[++Bcnt] = A[lca];
            if (Bcnt < 4) {
                printf("No\n");
            } else {
                sort(B+1, B+1+Bcnt);
                for (i = 4; i <= Bcnt; ++i)
                    if (SC(ll, B[i-3])+B[i-2]+B[i-1] > SC(ll, B[i])) break;
                if (i == Bcnt+1)
                    printf("No\n");
                else
                    printf("Yes\n");
            }
            for (a = x, i = 1; i <= X; ++i)
                mk[a] = 0, a = dad[a];
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/ghcred/p/9497341.html

时间: 2024-07-30 14:00:33

比赛-thh学长的训练赛 (Aug 16, 2018)的相关文章

比赛-CioCio的训练赛 (Aug 18, 2018)

1.) wjj 的子集序列 暴力二进制枚举子集打表,然后可以发现答案就是最大值.根据公式推一波也行吧-- #include <cstdio> #include <stack> #include <ctype.h> using namespace std; typedef long long ll; template<typename T> void rd(T &num) { char tt; bool flag = 0; while (!isdigi

比赛-模拟赛 (Aug 18, 2018)

1.) 小X的质数 线性筛就可以了.由唯一分解定理,如果 $ x = p_a \cdot p_b $ ,那么 \(x\) 也一定只能这样分解质因数.所以 \(x\) 也是符合题目条件的数. #include <cstdio> #include <ctype.h> #include <stack> using namespace std; template<typename T> void rd(T &num) { char tt; while (!i

最后一周第二天训练赛之第二题

试题: B - B Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Practice SPOJ ICODER Description Mathews uses a brand new 16-bit instruction processor. (Yeah i am being sarcastic!). It has one register (say R) and it su

Dream_Chaser队训练赛第一场 I题

Dream_Chaser队训练赛第一场 I题 题目来自2012成都区域赛 I - Count Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 4472 Description Prof. Tigris is the head of an archaeological team who is currently in charge of a

2017后期 第 1 场训练赛

题目依次为 NKOJ 上 P3496 P4236 P3774 P2407 1.数三角形 方法很多, 比如推出三边 x y z 的限制关系, 然后加加减减得到计算式子 不过也可以用观察法, 暴力计算出 n 为 1 至 13 对应的结果为: 0 0 0 1 3 7 13 22 34 50 70 95 125 相邻两数差为: 0 0 1 2 4 6 9 12 16 20 25 30 这些相邻两数相邻差又为: 0 1 1 2 2 3 3 4 4 5 5 找到规律了, 如果结果第 i 项为第 i - 1

HDU-4930 Fighting the Landlords 多校训练赛斗地主

只需要判断一个回合就可以了,枚举判断可以一次出完所有牌或者大过对面的牌的可能,注意的是4张相同的牌带两张牌的话是可以被炸弹炸的. #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <queue> #include <algorithm> #include <cstdlib> #include <ioma

CSU-ACM2014暑假集训基础组训练赛(1) 解题报告

•Problem A HDU 4450                 水题,签到题 水题..没啥好说的.给大家签到用的. 1 #include <cstdio> 2 int main(){ 3 int n,a,ans; 4 while(scanf("%d",&n),n){ 5 ans = 0; 6 for(int i = 0;i < n;i++){ 7 scanf("%d",&a); 8 ans += a*a; 9 } 10 pr

cumt训练赛题解

2017年4月3日 cumt2017春季--训练赛(1) A.HihoCoder 1339 (dp) 思路: 比较清晰呢,就是个dp吧.定义一下状态,dp[i][j]:前i个骰子,扔出点数和为j的方案数.然后不就很好写了嘛递推式,dp[i][j] = dp[i - 1][j - k](1<=k<=6). 好像题就做完了诶,可是窝wa了两发什么鬼.第一发爆int,第二发爆longlong,这里的trick就是方案数可能非常大,所以这题的应该把状态定义为dp[i][j]:前i个骰子,扔出点数和为j

CSU-ACM暑假集训基础组训练赛(4)解题报告

•Problem A SPOJ SUB_PROB   AC自动机 •题意: 给定一个长为M(M≤100000 )的文本串,和N(N≤1000)个长度不超过2000的模式串,问每个模式串是否在文本串中出现过? •几乎和周一课件上的第一个例题一模一样.. •把文本串丢到AC自动机里面去跑. •注意: •1.可能有两个相同的模式串(略坑吧.) •2.一个模式串可能是另一个模式串的后缀,即如果一个点的fail指针指向的点是一个“危险节点”,那么它本身也是一个“危险节点”. 1 #include <ios