题解 「THUPC 2021」挑战图灵奖
真·遇到钓鱼题就只会被钓
因为官方题解不太好找所以放一下官方题解
作者: 程泽瑞
题目简述
一个环上有\(n\)个点,要求任意连续的\((k+1)\)个点不同色,求最少颜色数。
敏珂同学会给你一个答案,你只需要判断这个答案是大了/小了/正好/无法判断。
数据范围:\(1 \le n \le 10^{10^6}\) 且 \(1 \le k \le 100\)
子任务设计
对于所有数据,满足\(n \le 10^5\) 或 \(n \ge 10^{100}\)
题解
这道题的题面主要起迷惑作用 (不会吧不会吧,不会有人真的对于 $n \ge 10^{100} $ 的部分输出无法判断了吧)
实际上,只需要解决敏珂同学所声称解决的问题,然后比较敏珂的答案和实际答案就可以了。
首先,对于\(n \le 2k+1\)的情况,任意两个点都不可以同色,于是最少颜色数就是\(n\) (注意 \(n \le k\)的情况);
否则,既然要求连续\((k+1)\)个点不同色,那么这道题的答案至少为\((k+1)\),
可以发现,将\(n\)个点按照\((k+1)\)的长度分成若干段,需要\(m = \lfloor \frac{n}{k+1} \rfloor\)段,还剩下$ n \bmod (k+1)$ 个点没有分配颜色;
对于没有分配颜色的点,可以把它们均匀分配在这些段中,而答案就是最长段长度的最小值,也就是$ k+1+ \lceil \frac{ n \bmod (k+1)}{m} \rceil $
因此,我们只需要计算出$ ans = k+1+ \lceil \frac{ n \bmod (k+1)}{m} \rceil $ ,然后与敏珂的答案作比较就可以了。
同时,可以注意到当$n \ge k^2+k $ 时,答案一定为$ k+1 $ 或 \(k+2\) (若\(n\)能整除\((k+1)\),则答案为前者,反之为后者)
因此,对于较大的\(n\)只需要做一次高精度模单精度(扫一遍即可),较小的\(n\)直接计算即可,时间复杂度 \(O\left(\log{n} \right)\)。
其实,这道题完全可以把\(k\)出到\(10^{10^6}\) 变成一道FFT题,或者把\(k\)出大一点变成高精度除法题,
但良心出题人为了大家签到愉快就没有这么做啦,希望大家签到愉快~
艹我真的输出无法判断了
一个贪心的思路是每 \(k\) 个一段,每段都 \(1,2,\cdots,k\) 这样放
最后会剩下 \(n\bmod (k+1)\) 个数(注意不是 \(\bmod k\),我 tm 到做自闭都没注意到)
那么如果再为这些数每个都新开一个颜色,打个表会发现是不优的
前面形成了若干个段,段长都是 \(k+1\),所以相邻断点之间互不影响
那么可以将多出来的这些数均匀分布到断点之中,需要额外添加的颜色数是 \(\frac{n\bmod (k+1)}{\lfloor \frac{n}{k+1} \rfloor}\)
注意当 \(n\leqslant 2k+1\) 时需要的颜色数是 \(n\)
艹如果真的考这种题的话会出人命的
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k, x;
int len1, len2;
char s[N], t[N];
namespace force{
int col[N], lim;
vector<int> e[N];
bool dfs(int u) {
if (u>=n) {
for (int i=0; i<n; ++i)
for (auto v:e[i])
if (col[v]==col[i])
return 0;
return 1;
}
for (int i=1; i<=lim; ++i) {
col[u]=i;
if (dfs(u+1)) return 1;
}
return 0;
}
bool check(int mid) {lim=mid; return dfs(0);}
void solve() {
for (int i=0; i<n; ++i) {
for (int j=1,v; j<=k; ++j) {
v=(i-j+n)%n;
e[i].push_back(v);
v=(i+j)%n;
e[i].push_back(v);
}
}
int l=1, r=8, mid;
while (l<=r) {
mid=(l+r)>>1;
if (!check(mid)) l=mid+1;
else r=mid-1;
}
cout<<l<<endl;
}
}
void check(int t, int x) {
if (t==x) puts("Correct, but it doesn't necessarily mean that you can win the Turing Award.");
else {
puts("Wrong, don't cheat me, you are too far away from the Turing Award.");
puts(x<t?"0":"1");
}
exit(0);
}
signed main()
{
scanf("%s%d%s", s+1, &k, t+1);
len1=strlen(s+1); len2=strlen(t+1);
if (len2>3) check(0, 1);
for (int i=1; i<=len2; ++i) x=x*10+t[i]-'0';
if (len1>5) {
for (int i=1; i<=len1; ++i) n=(n*10+s[i]-'0')%(k+1);
if (!n) check(k+1, x);
else check(k+2, x);
}
else {
for (int i=1; i<=len1; ++i) n=n*10+s[i]-'0';
if (n<2*k+1) check(n, x);
int rest=n%(k+1), m=n/(k+1), t=rest?(rest-1)/m+1:0;
check(k+1+t, x);
}
return 0;
}