2021“MINIEYE杯”中国大学生算法设计超级联赛(2)【解题报告】

本文涵盖了多个算法和数据结构问题的解决方案,包括立方体内的正三角形计数、树链剖分、区间加二次函数、不同数字的异或和、字符串处理以及课程安排优化。每个问题都提供了详细的分析和高效代码实现,展示了如何运用数学和数据结构技巧来解决复杂问题。

Replay

在这里插入图片描述

Problem A. I love cube

题目大意

T T T 组输入,每组一个整数 n n n.

求在 ( n − 1 ) 3 (n-1)^3 (n1)3 的正方体中,存在多少个合法正三角形.

合法三角形:三角形的三个整顶点都在正方体内或正方体上,且三条边要至少平行于一个基平面.

1 ≤ T ≤ 1 0 5 , 1 ≤ n ≤ 1 0 18 1 \leq T \leq 10^5, 1 \leq n \leq 10^{18} 1T105,1n1018.

分析

XJ直接推了个柿子 A N S = 8 ∑ i = 1 n − 1 i 3 \begin{aligned} ANS = 8\sum_{i=1}^{n-1}{i^3} \end{aligned} ANS=8i=1n1i3.

代码实现

#include <bits/stdc++.h>
using namespace std;

inline int read()
{
   
   
    int x = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
    {
   
   
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch))
    {
   
   
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

typedef long long ll;
typedef unsigned long long ull;

const int M = (int)1e5;
const int N = (int)1e5;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;

ll quick(ll a, ll b, ll p = mod)
{
   
   
    ll s = 1;
    while(b)
    {
   
   
        if(b & 1) s = s * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return s;
}

ll inv(ll n, ll p = mod)
{
   
   
    return quick(n, p - 2, p);
}

void work()
{
   
   
    ll n; scanf("%lld", &n);
    if(n == 1)
    {
   
   
        printf("0\n");
        return;
    }
    --n;
    n = n % mod;
    ll ans = n * (n + 1) % mod * inv(2) % mod;
    ans = ans * ans % mod;
    ans = ans * 8 % mod;
    printf("%lld\n", ans);
}

int main()
{
   
   
//    cout << 5 * log2(inf) * (M + 2 * N) << "\n";
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);
    int T; scanf("%d", &T);
    for(int ca = 1; ca <= T; ++ca)
    {
   
   
//        printf("Case #%d:\n", ca);
        work();
    }
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
    return 0;
}

Problem B. I love tree

题目大意

n n n 个点的树, q q q 次操作,每次操作分两种.

1    a    b 1 \; a \; b 1ab 表示把从点 a a a 到 点 b b b 这条链上的点加二次函数 x 2 x^2 x2.

2    a 2 \; a 2a 表示查询点 a a a 的值.

1 ≤ n , q ≤ 1 0 5 , 1 ≤ a , b ≤ n 1 \leq n, q \leq 10^5, 1 \leq a, b \leq n 1n,q105,1a,bn.

分析

显然就是树链剖分 + 区间加二次函数.

因为中间的运算结果爆掉 ll,查了 n n n 久…

代码实现

#include <bits/stdc++.h>
using namespace std;

template <typename T>
void read(T& n)
{
   
   
    n = 0; int f = 1; char ch = getchar();
    while(!isdigit(ch)) {
   
   if(ch == '-') f = -1; ch = getchar();}
    while( isdigit(ch)) {
   
   n = n * 10 + ch - '0', ch = getchar();}
    n *= f;
}

template <typename T>
void print(T n)
{
   
   
    if(n < 0) putchar('-'), n = -n;
    if(n > 9) print(n / 10);
    putchar(n % 10 + '0');
}

template <typename T>
void print(T n, char ch)
{
   
   
    print(n), putchar(ch);
}

typedef __int128 ll;

const int M = (int)1e5;

namespace TA
{
   
   
    struct TA
    {
   
   
        int n; ll tree[M + 5];
        void init(int _n) {
   
   n = _n; for(int i = 0; i <= n; ++i) tree[i] = 0;}
        inline int lowbit(int n) {
   
   return n&-n;}
        void add(int a, ll b) {
   
   for(int i = a; i <= n; i += lowbit(i)) tree[i] += b;}
        ll ask(int r) {
   
   ll s = 0; for(int i = r; i; i -= lowbit(i)) s += tree[i]; return s;}
    } t[5];

    void getUpdate(ll d[], ll b[], ll len)
    {
   
   
        d[0] = b[0] + b[1] + b[2];
        d[1] = -2 * b[0] - b[1] + b[2];
        d[2] = b[0];
        d[3] = -b[0] - (len + 1) * b[1] - (len + 1) * (len + 1) * b[2];
        d[4] = 2 * b[0] + (2 * len + 1) * b[1] + (2ll * len * len + 2 * len - 1) * b[2];
        d[5] = -b[0] - len * b[1] - len * len * b[2];
    }

    void add(int l, int r, ll b[])
    {
   
   
        ll d[6]; getUpdate(d, b, r - l + 1);
        ll p[6] = {
   
   l, l + 1, l + 2, r + 1, r + 2, r + 3}, pw[6] = {
   
   1, 1, 1, 1, 1, 1};
        for(int j = 1; j < 5; ++j)
        {
   
   
            for(int k = 0; k < 6; ++k)
            {
   
   
                t[j].add(p[k], pw[k] * d[k]);
                pw[k] *= p[k];
            }
        }
    }

    void getQuery(ll d[], ll r)
    {
   
   
        d[1] = r * r * r + 6 * r * r + 11 * r + 6;
        d[2] = -(3 * r * r + 12 * r + 11);
        d[3] = 3 * r + 6;
        d[4] = -1;
    }

    ll ask(int r)
    {
   
   
        ll d[5], s = 0; getQuery(d, r);
        for(int i = 1; i < 5; ++i) s += d[i] * t[i].ask(r);
        s /= 6;
        return (s + t[0].ask(r));
    }

    ll ask(int l, int r)
    {
   
   
        return ask(r) - ask(l - 1);
    }
}

namespace
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值