首页 > 其他分享 >砝码称重 题解

砝码称重 题解

时间:2023-09-22 20:44:06浏览次数:33  
标签:10 log int 题解 复杂度 枚举 砝码 转移 称重

砝码称重 题解

前言

这道题时限完全可以开到 1s,空间也开不到 1024kb

白想那么多优化(

不过这个复杂度可能是目前来看最合理(算出来保证能过)的。

题意简述

有一个长度为 \(n\) 的序列 \(a\),有两种操作:

  1. 把 \(l\) 到 \(r\) 的所有数改为 \(x\);
  2. 查询用 \(l\) 到 \(r\) 的所有数(每个数可以用无数次)能否通过相加得到 \(x\)。

保证 \(a_i\) 和所有操作 \(1\) 的 \(x\) 总共最多不超过 \(10\) 种数。

思路

首先看到这个不超过 \(10\) 种数,肯定会想到状压。因为每个数可以用无数次,所以我们只需知道 \([l, r]\) 内数的集合是什么。这个可以利用线段树来做。

然后就是一个背包了。我们设 \(f_{s, i}\) 表示用集合 \(s\) 中的数,\(i\) 能否被表示。设我们现在用 \(s\) 中的 \(x\) 来转移,有

\[f_{s, i} = f_{s, i} \lor f_{s, i-x} \]

当然,这里对于每一个 \(i\),必须用 \(x\) 的每个倍数转移一次,因为 \(x\) 可以用无数次。

直接枚举倍数感觉非常过不去这里给出第一个优化。我们发现这里的转移很多都是重复的,比如用 \(3x\) 转移的部分,在 \(2x\) 和 \(x\) 转移之后就都已经被转移了。所以可以用类似二进制拆分的思想,每次用 \(2^i\) 倍的 \(x\) 去转移,这样就可以保证不重不漏,并且每个 \(x\) 转移的复杂度优化到了 \(O(m \log m)\)。

然后还是感觉很寄我们发现每次的转移相当于是把数组整体右移 \(2^ix\),所以可以用 bitset 优化。这样,不仅时间复杂度会除以 \(64(32)\),空间复杂度也会小很多(虽然开 bool 数组也就多了 \(8\) 倍空间)。

然后我们其实就可以枚举集合,然后用集合内的数来求 \(f\) 数组了。不过,如果每次都把集合内的数都枚举一次,算出来的复杂度看上去还是过不了($O(\frac{1}{64(32)} 2^{k} k m \log m) $,虽然肯定跑不满,而且事实上可以过)。

然后我们发现,很多转移还是冗余的,因为在我们求新的集合 \(s\) 的 \(f\) 时,比它小的集合的 \(f\) 已经求出来,而且其中一定有 \(s\) 的子集。因此,我们每次只需要用 \(s\) 的二进制数最高位代表的数来转移就行了。这样,\(k\) 的枚举就没了,最终复杂度 \(O(\frac{1}{64(32)} 2^{k} m \log m)\),算出来是 \(5 \times 10^7\) 左右,完全可以过。

线段树自带的复杂度是 \(O(n \log n)\),所以总复杂度就是 \(O(\frac{1}{64(32)} 2^{k} m \log m + n \log n)\),仍然超不过 \(10^8\)。

后记

其实 dp 部分我想麻烦了,并不需要枚举每个倍数,只需要向前转移就行,这样在枚举到 \(i\) 之前就可以转移完 \(i\) 了,不过这样也就不能整体右移了所以我的复杂度还是少一半

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10, M = 1030, MM = 1e5+10;

int read() {
	int x = 0; char ch = getchar();
	while(ch<'0' || ch>'9') ch = getchar();
	while(ch>='0'&&ch<='9') x = x*10+(ch-48), ch = getchar();
	return x; 
}
bitset<100010> f[1030];

struct Question{
	int op, l, r, x;
}q[N];

int n, m, Q;

int vis[MM];//每个数的编号 
int a[N];
int nums[12], totn;

struct node{
	int st;//记录区间内的集合
	int tag;//标记被改成了哪个集合 
};
struct segmentTree{
	node tree[N<<2];
	#define ls tr<<1
	#define rs tr<<1 | 1
	void push_up(int tr) {
		tree[tr].st = (tree[ls].st | tree[rs].st);
	}
	void push_down(int tr) {
		if(tree[tr].tag) {
			tree[ls].st = tree[rs].st = tree[ls].tag = tree[rs].tag = tree[tr].tag;
			tree[tr].tag = 0;
		}
	}
	void build(int tr, int L, int R) {
		if(L == R) {
			tree[tr].st =  (1<<(vis[a[L]] - 1));
			return;
		}
		int mid = (L+R)>>1;
		build(ls, L, mid);
		build(rs, mid+1, R);
		push_up(tr);
	} 
	
	void modify(int tr, int L, int R, int lq, int rq, int val) {
		if(lq <= L && R <= rq) {
			tree[tr].st = tree[tr].tag = val;
			return;
		}
		push_down(tr);
		int mid = (L+R)>>1;
		if(lq <= mid) modify(ls, L, mid, lq, rq, val);
		if(mid < rq) modify(rs, mid+1, R, lq, rq, val);
		push_up(tr);
	}
	int query(int tr, int L, int R, int lq, int rq) {
		if(lq <= L && R <= rq) {
			return tree[tr].st;
		}
		push_down(tr);
		int mid = (L+R)>>1, ret = 0;
		if(lq <= mid) ret|=query(ls, L, mid, lq, rq);
		if(mid < rq) ret|=query(rs, mid+1, R, lq, rq);
		return ret;
	}
}seg; 
int main() {
//	cout << sizeof(f) << endl;
	n = read(), m = read(), Q = read();
	for(int i = 1; i<=n; ++i) {
		a[i] = read();
		if(!vis[a[i]]) {
			vis[a[i]] = ++totn;
			nums[totn] = a[i];
		}
	} 
	for(int i = 1; i<=Q; ++i) {
		q[i].op = read(), q[i].l = read(), q[i].r = read(), q[i].x = read();
		if(q[i].op == 1) {
			if(!vis[q[i].x]) {
				vis[q[i].x] = ++totn;
				nums[totn] = q[i].x;
			}
		}
	}
	seg.build(1, 1, n);
	f[0][0] = 1;
	for(int s = 1; s<=(1<<totn)-1; ++s) {
//		f[s][0] = 1;
		int pos = 0;
		for(int i = totn; i>=1; --i) {
			if((s>>(i-1)) & 1) {
////				int x = nums[i];
//				for(int j = nums[i]; j<=m; j*=2) {
//					f[s] |= (f[s]<<j);
//				}//旧的转移
				pos = i;//找到最高位在哪里 
				break;
			}
		}
		int ts = (s ^ (1<<(pos-1)));
		f[s] = f[ts];
		for(int j = nums[pos]; j<=m; j*=2) {
			f[s] |= (f[s] << j);
		}
	}
	for(int i = 1; i<=Q; ++i) {
		if(q[i].op == 1) {
			seg.modify(1, 1, n, q[i].l, q[i].r, 1<<(vis[q[i].x] - 1));
		} else {
			int st = seg.query(1, 1, n, q[i].l, q[i].r);
			if(f[st][q[i].x]) {
				puts("Yes");
			} else {
				puts("No");
			}
		}
	}
	return 0;
}

标签:10,log,int,题解,复杂度,枚举,砝码,转移,称重
From: https://www.cnblogs.com/frostwood/p/17723331.html

相关文章

  • 题解 AtCoder Beginner Contest 267 A~H
    ABC267solutionhttps://atcoder.jp/contests/abc267/ProblemA.Saturday题目描述输入一个表示星期的英文字符串,输出:还有多少天到星期六?solution依题意模拟。\(O(1)\)。ProblemB.Split?题目描述Robin有十个小球,如图排列成这样,然后Robin将一些球击飞了,他告诉你哪些......
  • Django跨域问题解决
    Django跨域问题解决今天在学习前端Vue框架的过程中,遇到了跨域相关问题问题1详情:AccesstoXMLHttpRequestat'http://127.0.0.1:8000/book/'fromorigin'http://localhost:63342'hasbeenblockedbyCORSpolicy:No'Access-Control-Allow-Origin'headerispre......
  • P5836 [USACO19DEC] Milk Visits S - 洛谷题解
     题目链接:[P5836] USACO19DEC] MilkVisitsS-洛谷|计算机科学教育新生态(luogu.com.cn)这道题可以用并查集来解决。题目中每个结点只有两个状态:H和G。那么我们可以推断出,只有当起点和终点间每个结点的状态相同但是起点(或者终点或起点到终点之间的某一点)与所需状态不同......
  • 苏格拉底问答,问题解决
     ......
  • 题解 P8670 [蓝桥杯 2018 国 B] 矩阵求和
    题目描述\[\sum_{i=1}^n\sum_{j=1}^n\gcd(i,j)^2\]具体思路solution1显然可以每次枚举\(\gcd(i,j)\)的取值。\[\sum_{k=1}^nk^2\sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)=k]\]令\(i=\lfloor\frac{i}{k}\rfloor\),\(j=\lfloor\frac{j}{k}\rfloor\)。\[\sum......
  • 【UVA 11175】From D to E and Back 题解(图论)
    取具有n个顶点和m条边的任意有向图D。你可以在以下方式。E将有m个顶点,每个顶点对应于D的每条边。例如,如果D有一条边uv,那么E将有一个叫做uv的顶点。现在,每当D有边uv和vw时,E就会有边顶点uv到顶点vw。E中没有其他边。你将得到一张E图,并且必须确定E是否有可能是某些有向图D的图的卧......
  • CF38H 题解
    problem&blog。远古场翻到的一个不错的题,提供一个好想很多的做法。求出任意两点的路径在全部路径中是第几个。然后随便找两个人,钦定他们是Au吊车尾与CuRank1。这样子就可以直接求出全部人可以是否可以拿AuAgCu了。然后就是傻子DP了,往状态里塞Au与Ag的人数,转移......
  • MUH and Cube Walls 题解
    MUHandCubeWalls前言怎么题解区同质化这么严重,16篇题解全是差分+KMP,就没有人写别的做法吗。(好吧其实是我一开始没想到差分才有了这么多奇怪做法)题目大意给定两个序列\(a,b\),求\(b\)在\(a\)中出现了多少次。我们定义\(b\)在\(a\)的出现次数为:\[\sum_{i=1}^n......
  • c#Winform窗体实际运行大小与size属性设置不一致问题解决
    privatevoidForm1_Load(objectsender,EventArgse){RectangleScreenArea=System.Windows.Forms.Screen.GetWorkingArea(this);//GetWorkingArea()检索显示器的工作区(工作区是显示器的桌面区域,不包括边界、标题栏、任务栏、停靠窗口和停靠......
  • vue学习问题解决
    报错errorComponentname"Index"shouldalwaysbemulti-wordvue/multi-word-component-names解决方法1、问题说明:在创建组件命名时,引用index.vue的过程中报错;2、报错的原因及分析:其一、报错的全称为:errorComponentname"index"shouldalwaysbemulti-wordvue/multi-w......