题解 「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;
}
posted @ 2022-03-19 16:49  Administrator-09  阅读(1)  评论(0编辑  收藏  举报