第二届图灵杯个人题解

  • 更好的阅读体验:点这

A 改bug

  • 签到题,没啥好说的,转义字符也没有,直接输出He1I0ωorld!即可
#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

const int N = 5e5 + 7;
const int M = 1e6 + 7;
const int mod = 1e9 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;

void solve() {
    printf("He1I0ωorld!");
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    // cin >> t;
    t = 1;
    while(t--) solve();
    return 0;
}

B 幂之和

  • 1x10001\le x \le 10001x1000,太小了,直接打表判定,下面程序没有任何优化,纯粹的暴力
#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

const int N = 5e5 + 7;
const int M = 1e6 + 7;
const int mod = 1e9 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
ll t, n, m, arr[N];
map<ll, bool> mp;
void solve() {
    for(ll i = 1; i <= 1000; ++i) {
        for(ll j = 1; j <= 1000; ++j) {
            ll a = i * i * i, b = j * j * j;
            if(a + b > 1000) break;
            mp[a + b] = 1;
        }
    }
    cin >> n;
    if(mp[n]) cout << "YES" << endl;
    else cout << "NO" << endl;
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    // cin >> t;
    t = 1;
    while(t--) solve();
    return 0;
}

C 求弧长

alt

  • 第一种情况,β=πα\beta = \pi - \alphaβ=πα,可在ABC\triangle ABCABC中,根据AC2=AB2+BC22ABBCcosαAC^2 = AB^2 + BC^2 - 2ABBC\cos{\alpha}AC2=AB2+BC22ABBCcosα求出α=arccosAB2+BC2AC22ABBC\alpha = arccos{\frac{AB^2 + BC^ 2 - AC^2}{2ABBC}}α=arccos2ABBCAB2+BC2AC2,再求出β=πα\beta = \pi - \alphaβ=πα。答案即为βr\beta * rβr
  • 第二种情况,利用GI2=r2+r22r2cosγGI^2 = r^2 + r^2 - 2r^2\cos{\gamma}GI2=r2+r22r2cosγγ=arccos2r2GI22r2\gamma = arc cos{\frac{2r^2 - GI^2}{2r^2}}γ=arccos2r22r2GI2可以求出γ\gammaγans=γrans = \gamma * rans=γr
  • 判定是哪种情况:
  • 设圆心为OOO,两条线同圆交点分别为P,QP,QP,Q;考虑PQO\triangle PQOPQO,可得PQ2=r2+r22r2cosβPQ^2 = r^2 + r^2 - 2r^2cos\beta PQ2=r2+r22r2cosβ;即PQ=2r2(1cosβ)PQ = \sqrt{2r^2(1 - cos\beta)}PQ=2r2(1cosβ)。判定ABABABPQPQPQ的大小即可,若PQ>ABPQ > ABPQ>AB则为情况二,否则为一
#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const double pi = acos(-1);
const int N = 5e5 + 7;
const int M = 1e6 + 7;
const int mod = 1e9 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
ll t;
double dis(double x1, double y1, double x2, double y2) {
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
void solve() {
    double xa, xb, xc, ya, yb, yc, r;
    scanf("%lf %lf %lf %lf %lf %lf %lf", &xa, &ya, &xb, &yb, &xc, &yc, &r);
    double AB = xb - xa, AC = dis(xa, ya, xc, yc), BC = dis(xb, yb, xc, yc);
    double theta = acos((AC * AC + BC * BC - AB * AB) / (2 * AC * BC));
    double gamma = pi - theta;
    double PQ = sqrt(2*r*(1 - cos(gamma)));
    if(PQ - AB >= eps) {
        double beta = acos((2 * r - AB * AB) / (2 * r));
        printf("%.6lf\n", beta * sqrt(r));
    } else {
        printf("%.6lf\n", gamma * sqrt(r));
    }
}

signed main() {
    cin >> t;
    while(t--) solve();
    return 0;
}

D 模模模

  • 直接猜lcm(a,b) + 1然后就AC了,设x=k1a+1=k2b+1x = k_1a + 1 = k_2b + 1x=k1a+1=k2b+1,所以k1a=k2bk_1a = k_2bk1a=k2b,若要使得xxx最小,则k1,k2k_1,k_2k1,k2尽可能小,即:lcm(a,b)=k1a=k2blcm(a,b) = k_1a = k_2blcm(a,b)=k1a=k2b最优,所以x=lcm(a,b)+1x = lcm(a,b) + 1x=lcm(a,b)+1
  • 补充一句:ab=gcd(a,b)lcm(a,b)a*b = gcd(a,b)*lcm(a,b)ab=gcd(a,b)lcm(a,b)。求lcmlcmlcm时一般先除后乘防溢出(即lcm=a/gcdblcm = a/gcd*blcm=a/gcdb而不是lcm=ab/gcdlcm = ab/gcdlcm=ab/gcd
#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

const int N = 5e5 + 7;
const int M = 1e6 + 7;
const int mod = 1e9 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll t, n, m, arr[N];

void solve() {
    ll a, b;
    cin >> a >> b;

    ll lcm = a / __gcd(a, b) * b;
    cout << lcm + 1 << endl;
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> t;
    // t = 1;
    while(t--) solve();
    return 0;
}

E Magic Stone Merging

  • k<0k < 0k<0的情况已经被叉了,现在k0k \ge 0k0

  • 很容易看出来是直接排,从大到小选,难度主要是后面的处理问题。这里稍微证一下怎么选

  • 假设对序列a1,a2ana_1,a_2\cdots a_na1,a2an考虑如何使得其结果更大。

  • val=(((a1a2+k)a3+k)<mtext> </mtext>)an+kval = (\cdots((a_1 a_2 + k) a_3 + k)\cdots)a_n + kval=(((a1a2+k)a3+k))an+k

    =a1a2an+k(a3a4an+a4a5an++an+1) = a_1a_2\cdots a_n + k(a_3 a_4 \cdots a_n + a_4a_5 \cdots a_n +\cdots +a_n+ 1)=a1a2an+k(a3a4an+a4a5an++an+1) =i=1nai+k((i=3ninai)+1)= \prod_{i = 1}^{n} a_i + k((\sum_{i = 3} ^ {n} \prod_{i}^{n}a_i) + 1) =i=1nai+k((i=3ninai)+1)

  • 显然只有k((i=3ninai)+1)k((\sum_{i = 3} ^ {n} \prod_{i}^{n}a_i) + 1)k((i=3ninai)+1)影响最后结果,这个式子aia_iai越靠后,影响越大。要使得这个式子最大,即从小往大排列aia_iai即可

  • 但是由于有取模的存在,直接使用堆去存权值会出问题。所以考虑设置顶标toptoptop去标记取模后的相对大小参考,并作为堆的key1key_1key1,权值作为堆的key2key_2key2

  • 多提一句顶标的事,顶标有且仅有在valvalval溢出modmodmod时使用,此后顶标作为排序参考,而key2key_2key2(即valvalval)仅用于去更新下一个valvalval。而在没溢出的时候,用不上顶标

#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

const int N = 5e5 + 7;
const int M = 1e6 + 7;
const int mod = 1e9 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
ll t, n, k, arr[N];
void solve() {
    cin >> n >> k;
    ll ans = 0;
    for(int i = 1; i <= n; ++i) cin >> arr[i];
    int top = 0, flag = 0;
    if(k > 0) {
        priority_queue<pair<ll, ll> , vector<pair<ll, ll> > , greater<pair<ll, ll> > > q;
        for(int i = 1; i <= n; ++i) q.push({0, arr[i]});
        while(q.size() > 1) {
            pair<ll, ll> a = q.top(); q.pop();
            pair<ll, ll> b = q.top(); q.pop();
            ll val2 = a.second * b.second + k;
            if(val2 >= mod || flag) q.push({++top, val2 % mod}), flag = 1;
            else q.push({0, val2});
        }ans = q.top().second;
    } else {
        for(int i = 1; i <= n; ++i) ans = ans * arr[i] % mod;
    }
    cout << ans % mod << endl;
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    // cin >> t;
    t = 1;
    while(t--) solve();
    return 0;
}

F 向上取整

  • (牛客sqrt形式不正确,这里改成图片)

alt (图片看不清参考下面)

  • 考虑取now=nnow = \lceil \sqrt{n}\rceilnow=n,对[now+1,n1][now + 1, n - 1][now+1,n1]中任意的iii进行arr[i]arr[n]1\lceil \frac{arr[i]}{arr[n]}\rceil \to 1arr[n]arr[i]1,再进行两次a[n]a[now]n1\lceil \frac{a[n]}{a[now]} \rceil \to \lfloor \sqrt{n}\rfloor \to 1a[now]a[n]n1。最后将n=now,now=nn= now,now = \lceil \sqrt{n}\rceiln=now,now=n。循环以上过程,直到合法停止(即now=n=2now = n = 2now=n=2时)
  • 下面证明以上构造合法:
  • 对于每次循环,对[now+1,n1][now + 1, n - 1][now+1,n1]使用一次操作,对nnn使用两次操作。即在一次循环内,使得k=nnowk = n - nowk=nnow个数构造为111,共使用k+1k + 1k+1次操作
  • 由题意得最大操作次数为n+6n + 6n+6,则以上循环次数不超过6+2=86 + 2 = 86+2=8次(前者为额外付出的预留次数,后者为序列a1,a2a_1,a_2a1,a2不需要操作剩余的操作次数)。即最坏情况下nmax82\sqrt[8]{n_{max}} \ge 28nmax2才符合条件,对于此题的nmax=3105n_{max} = 3*10^5nmax=3105是完全足够了的
#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const double pi = acos(-1);
const int N = 5e5 + 7;
const int M = 1e6 + 7;
const int mod = 1e9 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
ll t, n, arr[N], ans = 0;
vector<pair<int, int> > Move;
void tra(int l, int r) {
    for(int i = l + 1; i < r; ++i) {
        ++ans;
        Move.push_back({i, r});
    }ans += 2;
    Move.push_back({r, l});
    Move.push_back({r, l});
}

void solve() {
    cin >> n;
    ans = 0;
    Move.clear();
    for(int i = 1; i <= n; ++i) arr[i] = i;
    int now = ceil(sqrt(n));
    while(now >= 2 && n > 2) {
        tra(now, n);
        n = now;
        now = ceil(sqrt(n));
    }
    cout << ans << endl;
    for(auto i : Move) cout << i.first << " " << i.second << endl;
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//别用sc和read
    cin >> t;
    // t = 1;
    while(t--) solve();
    return 0;
}

G Color Sequence

  • dsu<mtext> </mtext>on<mtext> </mtext>treedsu\ on\ treedsu on tree,树上启发式合并板子题
  • 前置:树链剖分
  • 题意就是给你一颗树,有边权。对上面每一个节点uuu,统计uuu的子树中,对称的链最长是多长。
  • 对称:和回文意思一样,所有权中,出现奇数次的权最大有一个,其他权均为出现偶数次。考虑对权1w1\le w \le1w状压映射到2w2^{w}2w上,然后采用XorXorXor统计映射权,最后的ansansans若为2k2^k2k则为合法路径
  • 相同状压权值下,我们贪心的将其记录为深度更深的,可以证明答案是最大的
  • 对于节点uuu而言,它的答案有以下方法得到,取ans[u]=max(<mtext> </mtext>)ans[u] = max{(\cdots)}ans[u]=max()
    • 子树中最大的回文链长度
    • 选取节点uuu开始,与任一子链组成的回文链长度
    • 任意两个子链,经过uuu组成的回文链长度
  • 然后先跑一遍树剖,把轻重链分出来(dfsdfsdfs)
    • 先跑轻链,不留下它的影响,记录仅轻链答案
    • 再先跑重链,再跑轻链,记录重链所有的影响,算出含重链的答案
  • in[i]in[i]in[i]记录状压权值为iii的最深深度,Xor[i]Xor[i]Xor[i]统计rootiroot\to irooti路径上的状压权值
#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

const int N = 5e5 + 7;
const int X = 23;
const int M = 1e6 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int t, n, lca_dep = 0, maxx = 0, ans[N];
int hson[N], top[N], fa[N], dep[N], sz[N];
vector<pair<int, int> > G[N];
int Xor[1 << X], in[1 << X];

int dfs(int now, int depth) {
    dep[now] = depth + 1;
    hson[now] = 0, sz[now] = 1, sz[hson[now]] = 0;
    for(auto i : G[now]) {
        int to = i.first;
        if(dep[to]) continue;
        Xor[to] = (1 << i.second) ^ Xor[now]; 
        fa[to] = now;
        sz[now] += dfs(to, depth + 1);        
        if(sz[hson[now]] < sz[to]) hson[now] = to;
    }return sz[now];
}
void Clear(int now) {
    in[Xor[now]] = 0;
    for(auto i : G[now]) Clear(i.first);
}
void Updata(int now) {
    in[Xor[now]] = max(in[Xor[now]], dep[now]);
    for(auto i : G[now]) Updata(i.first);
}
void cal(int now) {
    if(in[Xor[now]]) maxx = max(maxx, in[Xor[now]] + dep[now] - lca_dep);
    for(int i = 1; i < (1 << X); i <<= 1) {
        if(in[i ^ Xor[now]]) maxx = max(maxx, dep[now] + in[i ^ Xor[now]]- lca_dep);
    }for(auto i : G[now]) cal(i.first);
}

void dsu(int now, bool ishson) {
    for(auto i : G[now]) {
        if(i.first == hson[now]) continue;
        dsu(i.first, false);
    }
    if(hson[now]) dsu(hson[now], true);
    lca_dep = dep[now] * 2;
    for(auto i : G[now]) maxx = max(maxx, ans[i.first]);
    for(auto i : G[now]) {
        if(hson[now] == i.first) continue;
        cal(i.first), Updata(i.first);
    }
    if(in[Xor[now]]) maxx = max(maxx, dep[now] + in[Xor[now]] - lca_dep);
    for(int i = 1; i < (1 << X); i <<= 1) {
        if(in[i ^ Xor[now]]) maxx = max(maxx, dep[now] + in[i ^ Xor[now]]- lca_dep);
    }
    if(!in[Xor[now]]) in[Xor[now]] = dep[now];
    ans[now] = maxx;
    if(!ishson) {
        maxx = 0;
        Clear(now);
    }
}

void solve() {
    cin >> n;
    for(int i = 2; i <= n; ++i) {
        int f, w;
        cin >> f >> w;
        G[f].push_back({i, w});
    }dfs(1, 1);
    dsu(1, true);
    for(int i = 1; i <= n; ++i) cout << ans[i] << " ";
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//别用sc和read
    t = 1;
    while(t--) solve();
    return 0;
}