首页 > 其他分享 >241110 noip 模拟赛

241110 noip 模拟赛

时间:2024-11-10 16:31:32浏览次数:1  
标签:tmp noip leq int pos 241110 second ans 模拟

省流:\(100+100+100+0\)。

T1

题意:给定长度为 \(n\) 的序列 \(a,b\),你需要找到一个字典序最大的序列 \(ans\) 使得对于所有的 \(1 \leq i \leq n\),\(ans_i = ((a_i \oplus ans_{i - 1}) + (b_i \oplus ans_{i - 1})) \% 2^{32}\),其中 \(ans_0 = ans_n\)。

\(1 \leq n \leq 3 \times 10^5,a_i,b_i \in [0,2^{32})\)。

官方做法:

从低到高枚举 \(ans_n\) 的每一位是什么,check 在这一位下是不是对的,如果对的就继续往下搜。时间复杂度为 \(\Theta(Sn \log V)\),其中 \(S\) 为合法解的个数。

这就可以通过了, 但是为什么是对的?

考虑 \(a + b\) 的最低位,当给 \(a,b\) 同时异或上某个数的时候,\(a+b\) 的最低位是不会变的,因此每次确定 \(ans_n\) 的最低位然后递推上去就是 \(\Theta(n \log V)\) 的。

我的抽象做法:

可以把 \(ans_n\) 的 \(32\) 位拆成 \(4\) 个 \(8\) 位,对于每 \(8\) 位暴力求出 \(tmp_i\) 表示假设 \(ans_0 = i\) 时 \(ans_q\) 的值。

稍微打表发现,\(tmp_i + tmp_{2^8j} + tmp_{2^{16}k}+tmp_{2^{24}l} = tmp_{i + 2^8j + 2^{16}k + 2^{24}l} + 3 \times tmp_0\)。其中 \(i,j,k,l < 2^8\),这样我们就能表示出所有的 \(tmp_i\)。如果一个 \(i\) 是合法的,说明 \(tmp_i = i\),那么把上面的柿子改写一下,即:

  • 若 \(i + 2^8j + 2^{16}k + 2^{24}l\) 为合法的 \(ans_0\) 当且仅当 \(tmp_i + tmp_{2^8j} + tmp_{2^{16}k}+tmp_{2^{24}l} = i + 2^8j + 2^{16}k + 2^{24}l + 3 \times tmp_0\)

可以移项,使每个 \(tmp_i\) 变为 \(tmp_i - i\),那么上面的柿子就变为 \(tmp_i + tmp_{2^8j} + tmp_{2^{16}k}+tmp_{2^{24}l} = 3 \times tmp_0\)。

暴力把第一第二项合并,第三第四项合并,然后在两个合并好的序列上双指针即可。时间复杂度 \(\Theta(n \sqrt[4]{V} + \sqrt{V})\)。

我的做法代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+5;
unsigned ans[N];
pair<int,int> x[N],y[N];
int n,a[N],b[N],tmp[N],sum[N],pos[N],genshin=0,res=114514;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n;
	for(int i=1; i<=n; i++) cin>>a[i]>>b[i];
	for(int i=0; i<256; i++) {
		ans[0]=i;
		for(int j=1; j<=n; j++) ans[j]=(a[j]^ans[j-1])+(b[j]^ans[j-1]);
		tmp[i]=ans[n];
	}
	for(int i=0; i<256; i++) {
		ans[0]=i*256;
		for(int j=1; j<=n; j++) ans[j]=(a[j]^ans[j-1])+(b[j]^ans[j-1]);
		tmp[i*256]=ans[n];
	}
	for(int i=0; i<256; i++)
		for(int j=0; j<256; j++)
			x[i+j*256]=make_pair(tmp[i]+tmp[j*256]-(i+j*256),i+j*256);
	for(int i=0; i<256; i++) {
		ans[0]=i*65536;
		for(int j=1; j<=n; j++) ans[j]=(a[j]^ans[j-1])+(b[j]^ans[j-1]);
		tmp[i]=ans[n];
	}
	for(int i=0; i<256; i++) {
		ans[0]=i*16777216;
		for(int j=1; j<=n; j++) ans[j]=(a[j]^ans[j-1])+(b[j]^ans[j-1]);
		tmp[i*256]=ans[n];
	}
	for(int i=0; i<256; i++)
		for(int j=0; j<256; j++)
			y[i+j*256]=make_pair(tmp[i]+tmp[j*256]-(i*65536+j*16777216),i*65536+j*16777216);
	swap(x,y);
	sort(x,x+65536);sort(y,y+65536);
	memset(pos,-1,sizeof(pos));
	sum[0]=1,pos[0]=0;
	for(int i=1; i<65536; i++) {
		if(y[i].first==y[i-1].first) sum[i]=sum[i-1],pos[i]=pos[i-1];
		sum[i]++;
		if(!~pos[i]||(a[1]^y[i].second)+(b[1]^y[i].second)>(a[1]^y[pos[i]].second)+(b[1]^y[pos[i]].second)) pos[i]=i;
	}
	int ps=65535;
	for(int i=0; i<65536; i++) {
		while(ps>=0&&x[i].first+y[ps].first>3*tmp[0]) ps--;
		if(ps>=0&&x[i].first+y[ps].first==3*tmp[0]) {
			if((a[1]^(x[i].second+y[pos[ps]].second))+(b[1]^(x[i].second+y[pos[ps]].second))>genshin) {
				genshin=(a[1]^(x[i].second+y[pos[ps]].second))+(b[1]^(x[i].second+y[pos[ps]].second));
				res=x[i].second+y[pos[ps]].second;
			}
		}
	}
	ans[0]=res;
	for(int i=1; i<=n; i++) ans[i]=(a[i]^ans[i-1])+(b[i]^ans[i-1]);
	for(int i=1; i<=n; i++) cout<<ans[i]<<'\n';
	return 0;
}

闲话:这题做了2h\oh\oh\oh

T2

原题:CF1967B2。

题意:给定两个整数 \(n,m\),你需要对满足以下条件的二元组 \((a,b)\) 计数:

  • \(1 \leq a \leq n,1 \leq b \leq m\)
  • \((a + b) \mid (b \times \gcd(a,b))\)

\(n,m \leq 10^6\)。

数学题。。。

老样子,稍微打表发现,若 \((a + b) \mid \gcd(a,b)^2\),则 \((a + b) \mid (b \times \gcd(a,b))\)。

所以只需要对满足 \((a + b) \mid \gcd(a,b)^2\) 的在合法范围的 \((a,b)\) 计数就行。

设 \(d = \gcd(a,b)\),则 \((a + b) \mid d^2\) 等价于 \((\frac{a}{d} + \frac{b}{d}) \mid d\),所以可以枚举 \(i\) 从 \(1 \to n\),\(j\) 从 \(1 \to m\),其中 \(i\) 表示 \(\frac{a}{d}\),\(b\) 表示 \(\frac{b}{d}\),由于 \(d\) 是 \(i + j\) 的倍数且 \(i \times d \leq n,j \times d \leq m\),所以合法的 \(d\) 的个数为 \(\lfloor \frac{\min(\lfloor \frac{n}{i} \rfloor,\lfloor \frac{m}{j} \rfloor)}{i + j} \rfloor\),发现这个柿子当 \(i > \sqrt n\) 或 \(j > \sqrt m\) 时答案为 \(0\),所以 \(i\) 只需枚举到 \(\sqrt n\),\(j\) 只需枚举到 \(\sqrt m\)。时间复杂度忽略求 \(\gcd\) 是 \(\Theta(\sqrt{nm})\)。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,m;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>t;
	while(t--) {
		cin>>n>>m;int ans=0;
		for(int i=1; (i-1)*(i-1)<=n; i++) {
			for(int j=1; (j-1)*(j-1)<=m; j++) {
				if(__gcd(i,j)>1) continue;
				ans+=min(n/i,m/j)/(i+j);
			}
		}
		cout<<ans<<'\n';
	}
	return 0;
}

闲话:这题做了1.5h\oh\oh\oh

T3

题意:给定一个长为 \(n\) 的排列 \(p\),你需要用最少的操作使得一个形如 \(1,2,3,\cdots,n\) 的排列变为排列 \(p\),输出操作次数和方案,定义一次操作如下:

选择一个位置 \(1 \leq pos <n\),把 \([1,pos]\) push到第一个队列,把 \([pos+1,n]\) push到第二个队列,有一个空序列 \(q\),每次你可以把一个非空队列的队首弹出并放在 \(q\) 的末尾,当两个队列都为空时,让 \(p\) 变成 \(q\)。

多测。\(n \leq 52,\sum n \leq 2 \times 10^5\)。

首先有个比较自然的思路是把 \(p_i\) 变成它的逆排列 \(p'\),即 \(p'_{p_i} = i\),这样题意就变为了用最少的操作使得 \(p'\) 变为 \(1,2,3,\cdots,n\) 的排列。

可以先把 \(p'\) 分为若干个连续的上升段,设有 \(cnt\) 个,则把前 \(\frac{cnt}{2}\) 个段扔进第一个队列,剩余扔进第二个。每次让两个队首的段合并放进 \(q\) 的末尾,可以证明一定有一种方法使得合并后的段是上升的,将两个队列队首的段再pop掉。如果第二个队列多了一个段就原封不动的放到 \(q\) 的末尾。这样每次段数会减少一半,可以证明是最少的步数,虽然我不会证

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int t,n,a[N],pos[N];
vector<char> ve[N];
pair<int,int> pr[N];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>t;
	while(t--) {
		cin>>n;
		for(int i=1; i<=n; i++) cin>>pos[i];
		for(int i=1; i<=n; i++) a[pos[i]]=i;
		int tot=0,turn=0;
		queue<int> q,q1,q2;
		while(tot!=1) {
			tot=0;turn++;
			int cnt=1,lst=1;
			for(int i=1; i<=n; i++) {
				if(a[i]>a[i-1]) continue;
				pr[++tot]=make_pair(lst,i-1);lst=i;
			}
			pr[++tot]=make_pair(lst,n);
			int pos1=1,pos2=tot/2+1;
			for(int i=1; i<=tot/2; i++) {
				for(int j=pr[pos1].first; j<=pr[pos1].second; j++) q1.push(a[j]);
				for(int j=pr[pos2].first; j<=pr[pos2].second; j++) q2.push(a[j]);
				while(!q1.empty()&&!q2.empty()) {
					if(q1.front()<q2.front()) q.push(q1.front()),q1.pop(),ve[turn].push_back('A');
					else q.push(q2.front()),q2.pop(),ve[turn].push_back('B');
				}
				while(!q1.empty()) q.push(q1.front()),q1.pop(),ve[turn].push_back('A');
				while(!q2.empty()) q.push(q2.front()),q2.pop(),ve[turn].push_back('B');
				pos1++,pos2++;
			}
			if(pos2<=tot) for(int j=pr[pos2].first; j<=pr[pos2].second; j++) q.push(a[j]),ve[turn].push_back('B');
			for(int i=1; i<=n; i++) a[i]=q.front(),q.pop();
		}
		turn--;
		cout<<turn<<'\n';
		for(int i=1; i<=turn; i++) {
			for(int j=0; j<ve[i].size(); j++) cout<<ve[i][j];
			cout<<'\n';ve[i].clear();
		}
		ve[turn+1].clear();
	}
	return 0;
}

闲话:这题只做了40min\kx。好像是某noi模拟t1。

T4

原题:2024年集训队互测Désive。

还不会。代码似乎很难写。

标签:tmp,noip,leq,int,pos,241110,second,ans,模拟
From: https://www.cnblogs.com/System-Error/p/18538156

相关文章

  • 每周算法2:数学+模拟+哈希表+栈+线性dp+贪心(简单)
    目录1.统计数字描述输入描述:输出描述: 题解2.两个数组的交集(哈希表)描述题解 3.点击消除(栈)描述输入描述:输出描述: 题解4.牛牛的快递(模拟+补充)描述输入描述:输出描述:题解 5.最小花费爬楼梯(简单线性dp)描述输入描述:输出描述:示例1题解6.数组中两......
  • noip模拟9
    来自官方题解.md星际联邦做法比较多,可以直接用Prim来做,但最简单的做法仍然是考虑Borůvka算法,我们在每一轮需要找到这个点向外到另一个联通块内的最小边。注意到当\(i\)固定时,最小边要么是前缀\([1,i)\)的最大值取到的,要么是\((i,n]\)内的最小值取到的。我们只需要......
  • 20241110
    T1前缀后缀首先\(q\)的数据范围是在搞笑,因为最多\(n\)次操作之后序列就没了。然后可以考虑\(f_{l,r}\)表示还剩\([l,r]\)时最多执行到了哪个操作。转移考虑下一个操作在左边做还是在右边做即可。可以对每个询问预处理出每个点左右第一个能接这个询问的点。时间复杂度......
  • AIGC时代算法工程师的面试秘籍(第二十五式2024.10.21-11.3) |【三年面试五年模拟】
    写在前面【三年面试五年模拟】旨在整理&挖掘AI算法工程师在实习/校招/社招时所需的干货知识点与面试经验,力求让读者在获得心仪offer的同时,增强技术基本面。欢迎大家关注Rocky的公众号:WeThinkIn欢迎大家关注Rocky的知乎:RockyDingAIGC算法工程师面试面经秘籍分享:WeThi......
  • P1002 [NOIP2002 普及组] 过河卒
    P1002[NOIP2002普及组]过河卒题目棋盘上AAA点有一个过河卒,需要走到目标BB......
  • 『模拟赛』多校A层冲刺NOIP2024模拟赛20
    RankmissionfailedA.星际联邦由于急着想切题,上来没细看就打了个树状数组上去,果然寄了,然后又各种方式优化,最终还是寄了,只有50pts。正解是学最小生成树时直接跳过的prim和菠萝,但是偏不这么做,而是找性质得出严格\(\mathcal{O(n)}\)的做法。首先比较平凡发现一个点的最......
  • 多校A层冲刺NOIP2024模拟赛20
    多校A层冲刺NOIP2024模拟赛20\(T1\)A.星际联邦\(25pts\)部分分\(25pts\):暴力建边后跑\(Kruskal\)或\(Prim\)。点击查看代码structnode{ intfrom,to,w;};inta[300010];vector<node>e;structDSU{ intfa[300010]; voidinit(intn) { for(inti......
  • 「NOIP2022」比赛
    洛谷。题目简述给定两个数列\(a,b\),有\(q\)次询问,每次询问\([L,R]\)的所有子区间\([l,r]\)的\(\max_{i=l}^ra_i\times\max_{i=l}^rb_i\)之和。其中,\(n,q\le2.5e5\)。分析这很像历史版本和,但是我们写过的只有一个数组\(a\)的。那么先从部分分开始。对于\(n,......
  • NOIP 模拟赛 Day 6
    T1每次赢的人放在最后,可以发现一轮过后相对位置不变,比赛模式图类似一个二叉树,每个人从最底层往上打,可以一层一层计算每个人打到这一层的概率,再往上的概率就是乘上另一个半场的每个人打到这一层的概率乘这个人赢过对方的概率的和,枚举\(x\)所在的每一个位置,复杂度是\(O(n^3)\),......
  • 大二上计组往年卷刷题之简单题部分 202411109
    2020年计组期末卷(非陈家骏班)1.请简述C++程序设计语言的设计理念、演化历程(包括主要的贡献者),并讨论Simula67在其中的作用。C++程序设计语言的设计理念C++的设计理念主要基于以下几个核心原则:高效地使用硬件:C++旨在保持与C语言的兼容性,使得C++代码与C代码运行时具有相似或更......