【2020.11.19提高组模拟】二次剩余two 题解

【2020.11.19提高组模拟】二次剩余two 题解

题目描述

\(n\)个二次函数,每个二次函数可以用两个值\(m,k\)描述:

\[f(x)=(x-m)^2+k \]

现在有\(q\)次操作:

1.插入一个二次函数\((m,k)\)

2.\((x,t)\)删除所有\(f(x)\le t\)的二次函数。

输出每次操作后还剩下的二次函数个数。

对于所有的数据,保证\(n,q,m,k,x,t\in [1,1e5]\)

以下是数据范围表格节选。

\(n,q\le\) \(m\le\)
1~4 20000 100000
5~6 100000 1000
7~10 100000 100000

Solution

考场上我先想到了对\(n^2\)的暴力进行卡常优化——结果就过了qaq老师不要重测卡我啊!

虽然这不是正解,但是我还是记录一下吧。

用了两个栈,先把开始的二次函数存到栈1。

对于操作1,直接入栈1。

对于操作2,扫描栈1内所有的二次函数,若无需删去就加入栈2,否则不加;再将栈2复制到栈1。

可以利用类似滚动数组的方法在两个栈之间回滚,省去复制操作;不要暴力清空栈,只要把size赋值为0就好。这样每次进行操作2需要遍历的次数仅仅为当前二次函数的个数。

Code-\(O(n^2)\)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define debug printf("Now is %d\n",__LINE__);
using namespace std;

template<class T>inline void read(T&x)
{
    char ch=getchar();
    int fu;
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    x*=fu;
}
inline int read()
{
	int x=0,fu=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
    int g=0;
    if(x<0) x=-x,putchar('-');
    do{G[++g]=x%10;x/=10;}while(x);
    for(re int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int n,q;
struct two
{
	LL m,k;
}sk[2][1000010];
int size[2];
int main()
{
	freopen("two.in","r",stdin);
	freopen("two.out","w",stdout);
	n=read();
	q=read();
	for(re int i=1;i<=n;i++) sk[0][i].m=read(),sk[0][i].k=read();
	size[0]=n;
	int op,x,t;
	bool flag=0;
	while(q--)
	{
		op=read();
		if(op==1)
		{
			sk[flag][++size[flag]].m=read();
			sk[flag][size[flag]].k=read();
			write(size[flag]);
		}
		else
		{
			x=read();
			t=read();
			for(re int i=1;i<=size[flag];i++)
			{
				if((x-sk[flag][i].m)*(x-sk[flag][i].m)+sk[flag][i].k>t) sk[!flag][++size[!flag]]=sk[flag][i];
			}
			size[flag]=0;
			flag=!flag;
			write(size[flag]);
		}
	}
	return 0;
}

记得开\(\texttt{long long}\)

Std

因为\(k\in \mathbb N^+\),所以先不考虑k。

\[f(x)=(x-m)^2\le t\\ \Rightarrow -\sqrt t \le x-m\le \sqrt t\\ \Rightarrow x-\sqrt t\le m\le x+\sqrt t \]

所以,可能会被删去的二次函数的\(m\)只可能在\(x\pm\sqrt t\)中。

而对于相同的\(m\),k小的应该越会被删去。

所以我们对每种m都用一个小根堆,扫描时只要扫\(x-\sqrt t\le m\le x+\sqrt t\)区间中的堆,然后尽量取出合法的二次函数即可。

时间复杂度:\(O((n+q)\log n+q\sqrt t)\)

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define debug printf("Now is %d\n",__LINE__);
using namespace std;

template<class T>inline void read(T&x)
{
    char ch=getchar();
    int fu;
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    x*=fu;
}
inline int read()
{
	int x=0,fu=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
    int g=0;
    if(x<0) x=-x,putchar('-');
    do{G[++g]=x%10;x/=10;}while(x);
    for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
LL n,q,ans;
priority_queue<int>a[100010];
int main()
{
//	freopen("two.in","r",stdin);
//	freopen("two.out","w",stdout);
	n=read();
	q=read();
	for(re int i=1,m,k;i<=n;i++) m=read(),k=read(),a[m].push(-k);
	ans=n;
	int op,x,y,l,r;
	while(q--)
	{
		op=read();
		if(op==1)
		{
			x=read();
			y=read();
			a[x].push(-y);
			ans++;
		}
		else
		{
			x=read();
			y=read();
			l=x-sqrt(y);
			r=x+sqrt(y);
			for(re int i=max(l,1);i<=r&&i<=100000;i++)
			{
				while(!a[i].empty())
				{
					if((x-i)*(LL)(x-i)-a[i].top()>y) break;
					a[i].pop();
					ans--;
				}
			}
		}
		write(ans);
	}
	return 0;
}

Attention

不要抱有侥幸心理。这次能过,下次还是会\(40pts\)乃至\(20pts\)的。

posted @ 2020-11-19 22:20  Vanilla_chan  阅读(176)  评论(0编辑  收藏  举报