第二届图灵杯个人题解
- 更好的阅读体验:点这
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 幂之和
- 1≤x≤1000,太小了,直接打表判定,下面程序没有任何优化,纯粹的暴力
#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 求弧长
- 第一种情况,β=π−α,可在△ABC中,根据AC2=AB2+BC2−2ABBCcosα求出α=arccos2ABBCAB2+BC2−AC2,再求出β=π−α。答案即为β∗r
- 第二种情况,利用GI2=r2+r2−2r2cosγ,γ=arccos2r22r2−GI2可以求出γ,ans=γ∗r
- 判定是哪种情况:
- 设圆心为O,两条线同圆交点分别为P,Q;考虑△PQO,可得PQ2=r2+r2−2r2cosβ;即PQ=2r2(1−cosβ)。判定AB和PQ的大小即可,若PQ>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+1,所以k1a=k2b,若要使得x最小,则k1,k2尽可能小,即:lcm(a,b)=k1a=k2b最优,所以x=lcm(a,b)+1- 补充一句:a∗b=gcd(a,b)∗lcm(a,b)。求lcm时一般先除后乘防溢出(即lcm=a/gcd∗b而不是lcm=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<0的情况已经被叉了,现在k≥0
-
很容易看出来是直接排,从大到小选,难度主要是后面的处理问题。这里稍微证一下怎么选
-
假设对序列a1,a2⋯an考虑如何使得其结果更大。
-
val=(⋯((a1a2+k)a3+k)⋯)an+k
=a1a2⋯an+k(a3a4⋯an+a4a5⋯an+⋯+an+1) =∏i=1nai+k((∑i=3n∏inai)+1)
-
显然只有k((∑i=3n∏inai)+1)影响最后结果,这个式子ai越靠后,影响越大。要使得这个式子最大,即从小往大排列ai即可
-
但是由于有取模的存在,直接使用堆去存权值会出问题。所以考虑设置顶标top去标记取模后的相对大小参考,并作为堆的key1,权值作为堆的key2
-
多提一句顶标的事,顶标有且仅有在val溢出mod时使用,此后顶标作为排序参考,而key2(即val)仅用于去更新下一个val。而在没溢出的时候,用不上顶标
#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形式不正确,这里改成图片)
(图片看不清参考下面)
- 考虑取now=⌈n⌉,对[now+1,n−1]中任意的i进行⌈arr[n]arr[i]⌉→1,再进行两次⌈a[now]a[n]⌉→⌊n⌋→1。最后将n=now,now=⌈n⌉。循环以上过程,直到合法停止(即now=n=2时)
- 下面证明以上构造合法:
- 对于每次循环,对[now+1,n−1]使用一次操作,对n使用两次操作。即在一次循环内,使得k=n−now个数构造为1,共使用k+1次操作
- 由题意得最大操作次数为n+6,则以上循环次数不超过6+2=8次(前者为额外付出的预留次数,后者为序列a1,a2不需要操作剩余的操作次数)。即最坏情况下8nmax≥2才符合条件,对于此题的nmax=3∗105是完全足够了的
#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 on tree,树上启发式合并板子题
- 前置:树链剖分
- 题意就是给你一颗树,有边权。对上面每一个节点u,统计u的子树中,对称的链最长是多长。
- 对称:和回文意思一样,所有权中,出现奇数次的权最大有一个,其他权均为出现偶数次。考虑对权1≤w≤状压映射到2w上,然后采用Xor统计映射权,最后的ans若为2k则为合法路径
- 相同状压权值下,我们贪心的将其记录为深度更深的,可以证明答案是最大的
- 对于节点u而言,它的答案有以下方法得到,取ans[u]=max(⋯)
- 子树中最大的回文链长度
- 选取节点u开始,与任一子链组成的回文链长度
- 任意两个子链,经过u组成的回文链长度
- 然后先跑一遍树剖,把轻重链分出来(dfs)
- 先跑轻链,不留下它的影响,记录仅轻链答案
- 再先跑重链,再跑轻链,记录重链所有的影响,算出含重链的答案
- in[i]记录状压权值为i的最深深度,Xor[i]统计root→i路径上的状压权值
#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;
}