首页 > 其他分享 >再解 [NOI2017] 整数

再解 [NOI2017] 整数

时间:2023-04-30 22:34:16浏览次数:51  
标签:势能 return 再解 int 整数 bl while NOI2017 dlt

提供一个来自 CF 大佬 adament 的有趣思路

首先我们知道的是一个只增加的 \(b\) 进制整数计数器,如果 \(b\) 是常数那么复杂度是均摊 \(O(1)\) 的。证明只需要考虑将 \(b\) 进制中为 \(b-1\) 的所有位的位数当成势能,那么每一次进位一定是 \(b-1\to 0\) 一定会消耗势能函数。

但这道题有加又有减,问题出在了可能有“退位” \(0\to b-1\),这时导致了势能函数的增加,这也是为什么均摊算法通常不能支持撤回。常规的做法是把加减分开考虑变成只加的情况。其实还有一种更简单的做法:我们不限制一个位上的值一定是 \([0,b)\) 的,而是 \((-b,b)\)。此时的进位/退位的形式都是 \(1-b\to 0\) 或 \(b-1 \to 0\)。我们将非零位的个数当成势能,这样进位/退位都会减小势能,而势能函数的总量就是 \(O(n)\) 的了。

然而现在要查询原数某一位,如何来还原原数的一位呢?我们发现进位对于下一位的影响总是 \(\pm 1\) 的,我们只需要找到我们当前查询的位的严格非零前驱,判断一下它是否为负,如果是当前位就需要 \(-1\) 然后输出。

具体到这题,我们需要将数 \(T\) 个压成一位,我实现压了 \(62\) 位。维护动态前驱用了 zkw 线段树上 finger search(只是减小常数)。输出时有个小技巧,负数的补码实际上就是它在二进制下的表示,所以提取负数的某一维可以直接跟正数一起位运算就可以了。

代码非常好写,跑得也飞快,复杂度 \(O(\frac{30n\log n}{T})\)。

#include <cstdio>
#include <bitset>
using namespace std;
int read(){
	char c=getchar();int x=0;bool f=0;
	while(c<48||c>57) f|=(c=='-'),c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	if(f) return -x;
	return x;
}
const int lT=62,sT=30;
typedef long long ll;
const ll Base=(1ll<<lT);
int q,dlt;
namespace ds{
	bitset<1<<20> f;
	void ins(int x){
		x+=dlt;
		while(!f[x]) f[x]=1,x>>=1;
	}
	void del(int x){
		x+=dlt;
		while(f[x]){
			f[x]=0;
			if(f[x^1]) return;
			x>>=1;
		}
	}
	int pre(int x){
		if(!x) return -1;
		x+=dlt-1;
		if(f[x]) return x-dlt;
		while(x>1){
			if((x&1)&&f[x^1]) break;
			x>>=1;
		}
		x^=1;
		if(!x) return -1;
		while(x<dlt){x<<=1;if(f[x^1]) x^=1;}
		return x-dlt;
	}
}
ll a[500003];
void upd(int x,ll v){
	if(!v) return;
	if(!a[x]) ds::ins(x);
	a[x]+=v;
	if(!a[x]) ds::del(x);
}
void inc(int x,int t){
	bool sig=0;
	if(x<0) sig=1,x=-x;
	int bl=t/lT,wid=lT*(bl+1)-t;
	if(wid>sT) wid=sT;
	if(sig){
		upd(bl,-((ll)(x&((1ll<<wid)-1))<<(t-bl*lT)));
		upd(bl+1,-(x>>wid));
	}
	else{
		upd(bl,(ll)(x&((1ll<<wid)-1))<<(t-bl*lT));
		upd(bl+1,(x>>wid));
	}
	ll tmp=0;
	int fl=0;
	do{
		upd(bl,tmp);
		tmp=a[bl]/Base;
		upd(bl,-Base*tmp);
		++bl;++fl;
	}while(fl<2||tmp);
}
bool qry(int x){
	int bl=x/lT,p=ds::pre(bl);x%=lT;
	if(p>=0&&a[p]<0) return (a[bl]-1)>>x&1;
	else return a[bl]>>x&1;
}
int main(){
	q=read();read();read();read();
	for(dlt=1;dlt<=(q>>1)+1;dlt<<=1);
	while(q--){
		int op=read(),x=read();
		if(op==1) inc(x,read());
		else printf("%d\n",qry(x));
	}
	return 0;
}

标签:势能,return,再解,int,整数,bl,while,NOI2017,dlt
From: https://www.cnblogs.com/yyyyxh/p/integer_solution.html

相关文章

  • 2023-04-29:一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。 给你一个整数
    2023-04-29:一个序列的宽度定义为该序列中最大元素和最小元素的差值。给你一个整数数组nums,返回nums的所有非空子序列的宽度之和由于答案可能非常大,请返回对109+7取余后的结果。子序列定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数组......
  • 2023-04-29:一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。 给你一个整数
    2023-04-29:一个序列的宽度定义为该序列中最大元素和最小元素的差值。给你一个整数数组nums,返回nums的所有非空子序列的宽度之和由于答案可能非常大,请返回对109+7取余后的结果。子序列定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数......
  • 数的范围 | 整数二分
    AC.789数的范围题目描述给定一个按照升序排列的长度为\(n\)的整数数组,以及\(q\)个查询。对于每个查询,返回一个元素\(k\)的起始位置和终止位置(位置从\(0\)开始计数)。输入格式第一行包含整数\(n\)和\(q\),表示数组长度和询问个数。第二行包含n个整数(均在\(1∼1000......
  • 数论基础1-整数的离散型
    例题一:例题二:例题三:例题四: ......
  • pwn刷题笔记(整数溢出)
    [BJDCTF2nd]r2t3写出反汇编代码如下:intds:__bss_start;intmain(){charbuf[0x408-4]intvar[4];my_init();puts("**********************************");puts("*WelcometotheBJDCTF!*");puts("[+]Ret......
  • # 快讯 | 整数智能携手格拉斯哥大学举办AI圆桌分享会
    算法、算力和数据作为人工智能发展的三大支柱,而获取高质量的数据已经成为人工智能工程化进程中的难题。如何能够寻找到与算法训练完美适配的数据集,在数据生产过程中有哪些常见的痛点?5月12日,由整数智能与格拉斯哥大学合作举办了一场人工智能领域的开放性讲座。曾参与编辑《人工智能......
  • 快速幂+大整数乘法
    (快速幂+位运算)\(0\lea,b\le10^9\\0\leqp\leq10^9\)快速幂:(1)取模运算法则(a+b)%p=(a%p+b%p)%p(a-b)%p=(a%p-b%p)%p(a*b)%p=(a%p*b%p)%p(2)快速幂可以在O(logk)内算出\(a^k\)modp的值先处理出:\(a^{2^0}\mod\p\)\(a......
  • 高精度乘一位整数
    求高精度数的n倍【问题描述】定义一个高精度数a,输出a的n(0<=n<=9)倍的值。a的长度不超过200.【输入输出描述】输入:两行,第一行为高精度数a,第二行为倍数n;输出:a的n倍的值【样例输入】122344445556667773【样例输出】36703333667000331#include<iost......
  • PTA1006 换个格式输出整数(C++)
    一、问题描述:让我们用字母 B 来表示“百”、字母 S 表示“十”,用 12...n 来表示不为零的个位数字 n(<10),换个格式来输出任一个不超过3位的正整数。例如 234 应该被输出为 BBSSS1234,因为它有2个“百”、3个“十”、以及个位的4。输入格式:每个测试输入包含1个测......
  • AcWing 242. 一个简单的整数问题 / 树状数组区间修改区间查询模板题
    AcWing242.一个简单的整数问题//实例化是抽象的天敌,是抽象的克星//通过公式sn=(i从1~n求积)di*(1+n)-(i从1~n求积)i*di//来计算前缀和,又(i从1~n求积)i*di不能由(i从1~n求积)di*(1+n)推出//所以除了维护d数组,还需维护......