每日杂题(1)

每日杂题(1)

2020.10.19 Omkar and Landslide(CF Global Round 10 F)

tags:思维,构造,数学

这题的关键在于一个非常神奇的结论: { h i } \{h_i\} {hi}中最多只会存在一组 h k − 1 = h k h_{k-1}=h_k hk1=hk的值,除该数对外均严格递增。

我一打开这道题就是毫无头绪。之后就算窃听到了李老板说的这个结论也不会证明。膜题解之。

首先如果从 h j h_j hj开始有若干块土能滑到 h j − 1 h_{j-1} hj1,并且滑到 h j − 1 h_{j-1} hj1之后能滑到 h j − 2 h_{j-2} hj2,那么可以看成直接把 h j h_j hj的土按顺序滑到 h j − 2 h_{j-2} hj2

先假设滑了若干次之后变成的数列为 { a i } \{a_i\} {ai}

那么思考这个过程什么时候才会停止。假设当前土滑到了 a k a_k ak,正准备滑到 a k − 1 a_{k-1} ak1,那么如果 a k > a k − 1 a_k>a_{k-1} ak>ak1则一定能滑下去。因为滑到 a k a_k ak的土的数量一定大于一,加上去之后差肯定大于等于 2 2 2了。

所以唯一停止的状态就是 a k = a k − 1 a_k=a_{k-1} ak=ak1,并且滑下来的土的数量为 1 1 1。这时 a k a_{k} ak就会增加 1 1 1。如果加一后 a k < a k + 1 a_{k}<a_{k+1} ak<ak+1,那么相等数对的数量就会减一;如果 a k = a k + 1 a_{k}=a_{k+1} ak=ak+1,我们可以看成相等的那对数往后移了一个位置,总数依旧不变。

而如果前面没有相等的数对,那这些土一定会全部滑到 a 1 a_1 a1。和上面分析一样,如果 a 1 = a 2 a_1=a_2 a1=a2则加一对数,不等的话则不加。

所以我们会发现相等的数最多只有一对。

那么发现这条规律之后就很简单了,直接用两个等差数列求和即可。

注意 a 1 a_1 a1的值可以用凑的去确定。因为 a 1 a_1 a1的值肯定要么等于一整个公差为1的等差数列求和的首项,要么等于刚刚求出来的首项的值加一。

代码丑。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    ll n; cin >> n;
    vector <ll> a(n);
    for (int i = 0; i < n; ++i) scanf("%lld", &a[i]);
    ll sum = 0;
    for (int i = 0; i < n; ++i) sum += a[i];
    ll a1 = (2 * sum + n - n * n) / (2 * n);
    if ((2 * a1 + n - 1) * n / 2 == sum)
    {
        for (int i = 0; i < n; ++i)
            printf("%lld ", a1 + i);
    }
    else
    {
        ++a1;
        ll d = (2 * a1 + n - 1) * n / 2 - sum;
        ll st = n + 1 - d;
        for (int i = 0; i < st - 1; ++i)
            printf("%lld ", a1 + i);
        for (int i = st - 1; i < n; ++i)
            printf("%lld ", a1 + i - 1);
    }
    return 0;
}

2020.10.20 最长异或值路径

tags:树上差分,trie

随便选取某一节点为根。然后对于任意路径 ( i − > j ) (i->j) (i>j),可以看成先从 i i i走到根再从根走到 j j j,两条路异或一下,从 l c a ( i , j ) lca(i,j) lca(i,j)到根的那部分被算了两遍,结果是0,只剩下 ( i − > j ) (i->j) (i>j)路上的边权异或值了。所以可以先预处理出根到所有节点的异或路径值,最后转化成trie的模板题。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 4e6 + 10;
int trie[MAXN][2], cnt[MAXN], idx;
struct edges
{
    int v, w;
};
void insert(int x)
{
    int p = 0;
    for (int i = 31; i >= 0; --i)
    {
        int u = x >> i & 1;
        if (!trie[p][u]) trie[p][u] = ++idx;
        p = trie[p][u];
    }
    cnt[p]++;
}
int find(int x)
{
    int p = 0, ans = 0;
    for (int i = 31; i >= 0; --i)
    {
        int u = x >> i & 1;
        if (!trie[p][!u]) ans = ans << 1 | u, p = trie[p][u];
        else ans = ans << 1 | (!u), p = trie[p][!u];
    }
    return ans;
}
int main()
{
    int n; cin >> n;
    vector <edges> G[n];
    for (int i = 1; i <= n - 1; ++i)
    {
        int u, v, w; scanf("%d%d%d", &u, &v, &w);
        G[u].push_back({v, w}); G[v].push_back({u, w});
    }
    vector <bool> vis(n, 0);
    vector <int> temp;
    function <void(int, int)> dfs = [&](int now, int val)
    {
        vis[now] = 1; temp.push_back(val), insert(val);
        for (auto &i : G[now])
            if (!vis[i.v]) dfs(i.v, val ^ i.w);
    };
    dfs(0, 0);
    int ans = 0;
    for (auto &i : temp) ans = max(ans, i ^ find(i));
    cout << ans << endl;
    return 0;
}

(其实是今天摸了 偷了一道之前写的题 作业太多做不完了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值