zoukankan      html  css  js  c++  java
  • 【牛客挑战赛48 E】速度即转发

    题目

    题目链接:https://ac.nowcoder.com/acm/contest/11161/E
    给定一个长度为 (n) 的数组 (a)。支持 (m) 次操作:

    • 给定 (l,r,k),求 ([0,10^5]) 内满足 (sum^{r}_{i=l}max(a_i-x,0)geq k) 的最大正整数 (x)
    • 给定 (x,y),将 (a_x) 修改为 (y)

    (n,m,a_ileq 10^5,kleq 10^{10})

    思路

    设值域为 (A)
    一个直观的想法是直接分块,对于每一个块维护 (sum[x],cnt[x]) 表示不小于 (x) 的数字之和以及出现次数,但是这样每次修改是 (O(A)) 的,无法接受。
    那么自然会想到再对值域分块,然后每次询问从大到小枚举。但是这样每次询问的复杂度又是 (O(sqrt{nA})) 的,依然不可以接受。
    所以考虑离线下来用带修莫队维护。设 (sum[x],cnt[x]) 表示值域在 ([xT,(x+1)T)) 区间的数字和以及出现次数,指针移动的复杂度是 (O(1)) 的,询问的时候易容从大到小枚举块,如果这个块符合要求,那么再枚举这个块内的所有数字即可。这样一次询问就是 (O(sqrt{A})) 的。复杂度就得以保证了。
    时间复杂度为 (O(n^{frac{5}{3}}+msqrt{A}))。我取序列块长为 (2000),值域块长为 (320)

    代码

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    typedef long long ll;
    
    const int N=100010,M1=2000,M2=320;
    int n,m,sb,a[N],bel[N],X[N],Y[N],cnt[M2+1],res[N];
    ll sum[M2+1],ans[N];
    
    struct Query
    {
    	int l,r,k,t,id;
    	
    	friend bool operator <(Query x,Query y)
    	{
    		if (bel[x.l]!=bel[y.l]) return bel[x.l]<bel[y.l];
    		if (bel[x.r]!=bel[y.r]) return bel[x.r]<bel[y.r];
    		return x.t<y.t;
    	}
    }ask[N];
    
    int read()
    {
    	int d=0; char ch=getchar();
    	while (!isdigit(ch)) ch=getchar();
    	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
    	return d;
    }
    
    void write(ll x)
    {
    	if (x>9) write(x/10);
    	putchar(x%10+48);
    }
    
    void add(int x)
    {
    	sum[bel[a[x]]]+=a[x];
    	cnt[bel[a[x]]]++;
    	res[a[x]]++;
    }
    
    void del(int x)
    {
    	sum[bel[a[x]]]-=a[x];
    	cnt[bel[a[x]]]--;
    	res[a[x]]--;
    }
    
    signed main()
    {
    	n=read(); m=read();
    	for (int i=1;i<=n;i++)
    	{
    		a[i]=read();
    		bel[i]=(i-1)/M1+1;
    	}
    	for (int i=1,j=0,opt;i<=m;i++)
    	{
    		opt=read();
    		if (opt==0) sb++,ask[sb]=(Query){read(),read(),read(),j,sb};
    		if (opt==1) j++,X[j]=read(),Y[j]=read();
    	}
    	memset(ans,-1,sizeof(ans));
    	sort(ask+1,ask+1+sb);
    	for (int i=0;i<=100000;i++) bel[i]=i/M2;
    	for (int o=1,l=1,r=0,j=0;o<=sb;o++)
    	{
    		int ql=ask[o].l,qr=ask[o].r,k=ask[o].k,t=ask[o].t;
    		for (;l>ql;l--) add(l-1);
    		for (;r<qr;r++) add(r+1);
    		for (;l<ql;l++) del(l);
    		for (;r>qr;r--) del(r);
    		for (;j<t;j++)
    		{
    			if (l<=X[j+1] && r>=X[j+1]) del(X[j+1]); 
    			swap(a[X[j+1]],Y[j+1]);
    			if (l<=X[j+1] && r>=X[j+1]) add(X[j+1]);
    		}
    		for (;j>t;j--)
    		{
    			if (l<=X[j] && r>=X[j]) del(X[j]);
    			swap(a[X[j]],Y[j]);
    			if (l<=X[j] && r>=X[j]) add(X[j]);
    		}
    		ll s=0,c=0;
    		for (int i=M2;i>=0;i--)
    		{
    			if (i*M2>=100000) continue;
    			s+=sum[i]; c+=cnt[i];
    			if (s-c*i*M2>=k)
    			{
    				for (int j=i*M2;j<min(100001LL,(i+1)*M2);j++)
    				{
    					if (s-j*c<k) { ans[ask[o].id]=j-1; break; }
    					s-=res[j]*j; c-=res[j];
    				}
    				if (ans[ask[o].id]==-1) ans[ask[o].id]=min(100001LL,(i+1)*M2)-1;
    				break;
    			}
    		}
    	} 
    	for (int i=1;i<=sb;i++)
    		if (ans[i]!=-1) write(ans[i]),putchar(10);
    			else putchar('-'),putchar(49),putchar(10);
    	return 0;
    }
    
  • 相关阅读:
    信号量Semaphore的使用
    Future和FutureTask
    阻塞队列和线程池
    Java多线程基础
    Java常用方法
    Scanner和BufferedReader的区别和用法
    QT学习笔记9:QTableWidget的用法总结
    WordPress 建站教程:新手搭建 WordPress个人博客图文教程(完全版)
    Let's do it
    jdk alternatives
  • 原文地址:https://www.cnblogs.com/stoorz/p/14584643.html
Copyright © 2011-2022 走看看