【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\)的。