专栏文章

题解:CF1970D3 Arithmancy (Hard)

CF1970D3题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miqbi1cq
此快照首次捕获于
2025/12/04 02:05
3 个月前
此快照最后确认于
2025/12/04 02:05
3 个月前
查看原文
写了一个不需要打表的程序,以 9843ms9843 \operatorname{ms} 获得了全球最劣解。
记号说明:
  1. f(i,j)f(i,j) 为第 ii 个与第 jj 个字符串拼接后的本质不同的子串个数。
D1 只需要随便打几个字符串就可以了。但 D2 就无法像这样直接随机。经过我的尝试,我的程序最多只能随机出 n=25n=25 的解。这引导我们去想更好的随机方式。
容易发现,我们如果每一次随机都重新计算 nn 个字符串,速度会很慢。于是我们可以考虑递推。每一次先构造好前面的字符串,再随机当前的字符串,直到当前字符串与前面的字符串的 ff,与前面都不同。
但是即使这样,直接随机字符串依然很困难,这主要体现在 22 个方面:
  1. 每一次计算 ff 的速度比较慢(O(nlogn)O(n \log n))。
  2. 字符串的格式不确定,随机状态非常多。
22 点都启发我们找到一个固定的字符串的格式,使得 ff 的计算也很方便。
X\text{X}O\text{O} 都很多的时候,ff 显然不方便计算,于是我们可以考虑比较极端的构造,例如全部都是 X\text{X} 或全部都是 O\text{O}。但是显然不满足条件。于是我们可以只改一个字符,从而使字符串都是形如 XO\text{XO} 后面跟上一堆 X\text{X}
对于这种字符串,我们容易检验 nn 比较小的时候是可以递推的,于是大胆猜测 n=1000n=1000 时递推不会错。
现在只要处理的问题就是:如何快速计算 ff
可以通过尝试小的例子来找出规律:
设第 ii 个字符串的长度为 xx,第 jj 个字符串的长度为 yy。那么:
f(i,j)={x(y+2)1xy(x+3)(y1)1otherwisef(i,j)=\begin{cases} x(y+2)-1 & x \ge y \\ (x+3)(y-1)-1 & \text{otherwise} \end{cases}
然后代码的实现是非常简单的。我们使用一个 map 来存储答案,每一次递推求答案的时候新开一个 map 来判断当前这个字符串放进答案中是否可行。如果可行,就把这个字符串与前面字符串的 ff 值存到答案里,否则就尝试更长的字符串。
最后的询问只要输出 map 里储存的两个字符串的编号即可。
CPP
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pii pair<int,int>
#define endl '\n'
#define pb push_back
#define ls(p) ((p)<<1)
#define rs(p) ((p)<<1|1)
#define lowbit(x) ((x)&(-(x)))
#define abs(x) ((x)>0?(x):(-(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define gc getchar
#define pc putchar
#define sz(v) ((int)(v.size()))
using namespace std;
mt19937_64 Rand(chrono::steady_clock::now().time_since_epoch().count());
inline int read(){
	int x=0,f=1;
	char ch=gc();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=gc();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=gc();}
	return x*f;
}
inline void print(int x){
	if (x<0) pc('-'),x*=-1;
	if (x<10) pc(x+'0');
    else print(x/10),pc(x%10+'0');
}
int work(int x,int y){// f 函数
	return x>=y?x*(y+2)-1:(x+3)*(y-1)-1;
}
int n;
unordered_map<int,pii> ans;
unordered_map<int,bool> mp;
const int N=1005;
int b[N];// b_i=len(a_i)
string a[N];
void init(){
	int now=1;
	for (int i=1;i<=n;i++){
		bool f=1;
		do{
			now++;//增加长度
			f=1;
			mp.clear();
			int cnt=work(now,now);
			if (ans.count(cnt)){
				f=0;
				continue;
			}
			mp[cnt]=1;
			for (int _=1;_<i;_++){
				cnt=work(b[_],now);
				if (ans.count(cnt)||mp.count(cnt)){
					f=0;
					continue;
				}
				mp[cnt]=1;
				cnt=work(now,b[_]);
				if (ans.count(cnt)||mp.count(cnt)){
					f=0;
					continue;
				}
				mp[cnt]=1;
			}
			if (f){// 如果可以,把当前字符串与前面的字符串的 f 值放进 ans 中
				ans[work(now,now)]={i,i};
				for (int _=1;_<i;_++){
					ans[work(b[_],now)]={_,i};
					ans[work(now,b[_])]={i,_};
				}
			}
		}while (!f);
		a[i]="XO";
		for (int _=1;_<=now-2;_++) a[i]+='X';//存储答案
		b[i]=now;
	}
}
void sol(){
	for (int i=1;i<=n;i++) cout<<a[i]<<endl;
	int q;
	cin>>q;
	while (q--){
		int x;
		cin>>x;
		cout<<ans[x].fi<<' '<<ans[x].se<<endl;
	} 
}
signed main(){
	cin>>n;
	init();
	sol();
	return 0;
}
温馨提示:如果你 TLE on #15,请不要使用 map,改成 unordered_map。

评论

0 条评论,欢迎与作者交流。

正在加载评论...