把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P5068 [Ynoi2015] 我回来了

题面传送门
居然一遍过我太惊讶了。
首先,题目中那个期望是假的,实际要求出现次数。
然后你会发现对于一个\(x\),答案不会超过\(\frac{n}{x}\)
那么总的答案就是\(O(nlogn)\)
你会发现这个\(n\)其实和\(m\)不同阶,这个\(n\)甚至两个log都可以过。
如果我们对于每一个答案的增加然后树状数组维护一下就可以两个log
那么我们只要找到\(n\)个区间内包含一个点的区间即可。
这个显然套路地线段树分治即可。
然后就可以复杂度\(O(nlog^2n+mlogn)\)了。
然而这个东西空间复杂度是\(O(nlog^2n)\)的。
没办法了,信仰跑吧。毕竟\(10^5\)不是很大。
code:

#include<cstdio>
#include<vector>
#define N 100039
#define l(x) x<<1
#define r(x) x<<1|1
#define I inline
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,op,x,y,z,h[N];
struct BIT{
	int f[N];
	I void get(int x,int y){while(x<=n) f[x]+=y,x+=x&-x;}
	I int find(int x){x>n&&(x=n);int ans=0;while(x) ans+=f[x],x-=x&-x;return ans;}
}f1,f2;
struct pai{int id,w;};
vector<pai> g[N*4];
I void get(int x,int y,pai z,int l=1,int r=n,int now=1){
	if(x<=l&&r<=y) return (void)(g[now].push_back(z));int m=l+r>>1;
	x<=m&&(get(x,y,z,l,m,l(now)),0);y>m&&(get(x,y,z,m+1,r,r(now)),0);
}
I void work(int now){
	int i;pai tmp;
	for(i=0;i<g[now].size();i++){
		tmp=g[now][i];
		if(tmp.w==h[tmp.id]){
		    while(f2.find(tmp.id*(h[tmp.id]+1))-f2.find(tmp.id*h[tmp.id])) h[tmp.id]++;
		    if(tmp.id*h[tmp.id]+1<=n)get(tmp.id*h[tmp.id]+1,min(tmp.id*(h[tmp.id]+1),n),(pai){tmp.id,h[tmp.id]});f1.get(tmp.id,h[tmp.id]-tmp.w);
		}
	}
	g[now].clear();
}
I void find(int x,int l=1,int r=n,int now=1){work(now);if(l==r) return;int m=l+r>>1;x<=m?(find(x,l,m,l(now))):(find(x,m+1,r,r(now)));}
int main(){
	freopen("1.in","r",stdin);
	register int i;scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) get(1,i,(pai){i,0}),f1.get(i,1);
	while(m--)scanf("%d",&op),op^2?(scanf("%d",&x),f2.get(x,1),find(x),0):(scanf("%d%d",&x,&y),printf("%d\n",f1.find(y)-f1.find(x-1)));
}
posted @ 2021-04-05 13:05  275307894a  阅读(52)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end