首页 > 其他分享 >[THUPC2024] 分治乘法

[THUPC2024] 分治乘法

时间:2024-02-09 10:11:06浏览次数:29  
标签:aa sz idx int 分治 ++ 集合 THUPC2024 乘法

[THUPC 2024 初赛] 分治乘法

题目描述

小艾想要挑战分治乘法。TA 将策略抽象成了如下问题:

现在给定一个目标集合 \(T\),该集合是 \(\{1,\dots,n\}\) 的一个子集(\(1\leq n\leq 5\times 10^5\))。你需要通过一系列操作构造一些集合最后得到 \(T\),具体来说有以下三种操作:

  • 创造一个大小为一的集合 \(|S|=1\)。
  • 将已经被构造出的两个不交集合 \(A, B\) 并起来,得到 \(A\cup B\)。
  • 将已经被构造出的一个集合 \(A\) 进行平移,也即 \(A+x = \{ a+x : a\in A \}\)。

一个已经被构造出的集合可以在之后被使用多次。同时你需要保证操作过程中出现的所有集合都是 \(\{1,\dots,n\}\) 的子集。

你的代价是构造出的所有集合的大小之和,你不需要最小化代价,只需要让代价控制不超过 \(5\times 10^6\) 即可。你用的操作数量也不应超过 \(10^6\)。

输入格式

第一行输入一个正整数 \(n\)。

接下来一行输入一个 01 串,长度为 \(n\),第 \(x\) 位为 1 表示 \(x\in T\),否则 \(x\notin T\),保证 \(T\) 非空。

输出格式

第一行输出一个正整数 \(m\) 表示你使用的操作数量。

接下来 \(m\) 行,每行描述一个操作,设第 \(i\) 次操作得到的集合为 \(T_i\):

  • 1 x 表示创造一个大小为一的集合 \(\{x\}\)。
  • 2 x y 表示将不交集合 \(T_x, T_y\) 并起来。
  • 3 x y 表示将已经被构造出的一个集合进行平移,也即 \(T_x+y\)。

你需要保证所有操作满足题目要求,并且最后一次操作产生的集合是 \(T\)。

赛时被蔡神秒了,%%%

首先考虑分治,代价是 \(O(nlog n)\) 的,大概多了一倍。而且没用操作3.

平移想到什么?分块!

考虑 B 个为一块,那么每一块总共有 \(2^B\) 种形态。设第 \(i\) 种形态出现位置序列为 \(f_i\),那么 \(\sum|f_i|=\frac nB\),用分治做法得到所有的 \(f_i\),代价为 \(O(\frac nB\log n)\),然后再平移得到所有的位置。

然后再用合并果子合并上去。集合大小的和是 \(n\),总共 \(B2^B\) 个集合,复杂度是 \(O(nlog(B2^b))=O(nB)\) 的。

代价复杂度可以平衡到 \(O(n\sqrt {\log n})\)

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5,B=5,M=5e6+5;
int n,p[M],sz[M],r,idx,op[M],aa[M],bb[M];
char s[N];
vector<int>g[N];
int haffman()
{
	if(!r)
		return 0;
	priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
	for(int i=1;i<=r;i++)
		q.push(make_pair(sz[p[i]],p[i]));
	while(q.size()>1)
	{
		pair<int,int>u=q.top(),v;
		q.pop();
		v=q.top();
		q.pop();
		op[++idx]=2,aa[idx]=u.second,bb[idx]=v.second;
		q.push(make_pair(u.first+v.first,idx));
	}
	return q.top().second;
}
int slv(vector<int>&g,int l,int r)
{
	if(l==r)
	{
		op[++idx]=1,aa[idx]=g[l]+1;
		sz[idx]=1;
		return idx;
	}
	int md=l+r>>1;
	int u=slv(g,l,md);
	int v=slv(g,md+1,r);
	sz[++idx]=sz[u]+sz[v];
	op[idx]=2,aa[idx]=u,bb[idx]=v;
	return idx;
}
int main()
{
	scanf("%d%s",&n,s);
	for(int i=0;i<n;i+=B)
	{
		int t=0;
		for(int j=0;j<B&&i+j<n;j++)
			t|=s[i+j]-48<<j;
		g[t].push_back(i);
	}
//	puts("NO!!!");
	for(int i=0;i<(1<<B);i++)
	{
		if(g[i].empty())
			continue;
		int x=slv(g[i],0,g[i].size()-1);
		if(i&1)
			p[++r]=x;
		for(int j=1;j<B;j++)
			if(i>>j&1)
				p[++r]=++idx,op[idx]=3,aa[idx]=x,bb[idx]=j,sz[idx]=sz[x];
	}
	int k=haffman(); 
	printf("%d\n",idx);
	for(int i=1;i<=idx;i++)
	{
		printf("%d %d ",op[i],aa[i]);
		if(op[i]^1)
			printf("%d",bb[i]);
		puts("");
	 } 
}

标签:aa,sz,idx,int,分治,++,集合,THUPC2024,乘法
From: https://www.cnblogs.com/mekoszc/p/18012356

相关文章

  • 打印九九乘法表
    需求打印九九乘法表代码实现packagecom.jichu.struct;publicclassForDemo03{publicstaticvoidmain(String[]args){//打印九九乘法表for(inti=1;i<10;i++){//i列for(intj=1;j<=i;j++){//j行int......
  • 机器学习之最小二乘法
    最小二乘法概述最小二乘法(又称最小平方法)是一种数学优化技术。它通过最小化误差的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数据,并使得这些求得的数据与实际数据之间误差的平方和为最小。最小二乘法还可用于曲线拟合。其他一些优化问题也可通过最小化能......
  • P4721 【模板】分治 FFT
    最具经济性的写法:\(\mathcalO(n^2)\)暴力拿下\(80\)分,遂跑路。一题多解了,分两部分:分治和多项式求逆。分治考虑cdq分治,每次把\(f_{l\dotsmid}\)和\(g_{1\dotsn-1}\)卷起来,贡献直接加到\(f_{mid+1\dotsr}\)里,要注意一下顺序,先递归左区间,再算当前区间,最......
  • THUPC2024-初赛
    哈哈,被干爆了。拖了cdqz哥后腿。题目使用协议来自THUPC2024(2024年清华大学学生程序设计竞赛暨高校邀请赛)初赛。以下『本仓库』皆指THUPC2024初赛官方仓库任何单位或个人都可以免费使用或转载本仓库的题目;任何单位或个人在使用本仓库题目时,应做到无偿、公开,严禁使用这......
  • 根号分治入门
    在vpcf的时候遇到的算法,当时看着就是题目很清楚,要不就是我不会的数据结果,要不就是算法,想了一会想不出直接去看题解了。现在补一下。根号分治虽然名字里面有“分治”但实际上和分治的关系并不大,根号分治更多的还是一种思想。根号分治的思想是将询问根据一个阈值设为\(S\)分为两......
  • 打印九九乘法表
    需求打印九九乘法表代码实现packagecom.jichu.struct;publicclassForDemo03{publicstaticvoidmain(String[]args){//打印九九乘法表for(inti=1;i<10;i++){//i列for(intj=1;j<=i;j++){//j行int......
  • GCD,乘法逆元
    最大公约数公约数:几个整数共有的约数。($\pm1是任何整数的公约数$)最大公约数:显而易见,所有公约数中最大的那个。欧几里得算法为了求最大公约数(常记为GCD),我们常用欧几里得算法。以两个数的最大公约数为例。设正整数a,b。不妨假设\(a>b\)。\[gcd(a,b)=gcd(b,a\mod\b)\]证明......
  • 【算法】树分治
    1.算法简介树分治(Treedivision),是处理树上路径类问题的算法。树分治又可以分为点分治与边分治。考虑这样一个问题:给定一棵有\(n\)个点的树,询问树上距离为\(k\)的点对是否存在。暴力的做法就是枚举两个点然后计算距离,统计答案。这样显然\(O(n^2)\)的。我们发现瓶颈在于......
  • 点分治
    点分治定义树上的分治先求一个点的答案,然后求子树树上距离小于等于k的点对数量枚举一个点p求解经过p的点对贡献,然后递归解决子树为了降低分治复杂度,要求重心,求重心要限定子树范围内,添加vis防止上访,求dis也要ans要减去在同一个子树重心的子树小于\(n/2\),所以调用递归......
  • CDQ分治
    CDQ分治引入偏序问题对于每个有序对\((a_i,b_i)\)求有多少个有序对\((a_j,a_j)\)\(a_i<a_j,b_i<b_j\)暴力\(O(n^2)\)按\(a\)排序,问题为求顺序对,cdq分治定义解决特定种类问题的算法,统计左区间对右区间的贡献,一个点所得贡献必然计算,且区间划分不重不漏实现要注意有......