2018 ICPC Shenyang G - Best ACMer Solves the Hardest Problem
做题一定要把计算复杂度算明白,其次就是空间复杂度
https://vjudge.net/contest/418609#problem/G
题目大意:
在平面上给定了n个点,然后有m个操作
1.在(x,y)增加一个点,权值设为w(保证原来是空的)
2.把(x,y)的点删掉(保证原来有点)
3.以(x,y)为圆心,\(\sqrt{k}\)为半径,作圆,把圆上的每个点的权值+w
4.以(x,y)为圆心,\(\sqrt{k}\)为半径,作圆,把圆上的每个点的权值加起来,输出答案
本题有多组数据,且强制在线
其中\(1\le k\le 10^7,\sum n,\sum m\le 10^6,0\le x,y\le 6000\)
本题给了12s,1GB内存,资源相当丰富,说明这个题可能做法比较暴力
首先,平面上的点可以用二维数组存储,约60006000
我们要存储点是否存在,以及点的权值,内存开销为60006000*(4+1)=1.8e8B
当一组数据的算法结束后,我们需要把每个存在的点的记录清空,复杂度为\(O(n+m)\),而不是将整个数组初始化
操作3.4要求我们遍历所有距离(x,y)为\(\sqrt{k}\)的整数点
一种方法是从\(1\)到\(\sqrt{k}\)枚举\(i\),然后计算\(\sqrt{k-i^2}\)是不是整数,如果是整数,就查看一下\((x+i,y+j)\)这个点
这个做法不容易优化,单次查询需要\(\sqrt(k)\)次
考虑最多有\(10^6\)次操作,需要计算约\(3e9\)次,然而只有12s,时间吃紧,给30s可能会稳一些
提交之后一般过不了,当然我不能排除有人使用计算机组成原理的知识进行玄学优化,说不定可以卡过
考虑所有距离(x,y)为\(\sqrt{k}\)的整数点的个数可能不会达到3000多个,我做了一个测试
从1到10000000遍历\(k\),看看\(k=i^2+j^2\)的整数解最多有几个,测试结果为192,远小于3000
由于k最大是1e7,所以我们可以在3200的范围内枚举\((i,j)\),把枚举到的点加入到编号为k的链表或向量中,这样只要我们在查找k对应的线性表即可,里面的点对都是距离为k的整数点。
预处理可能需要几秒,然后后面的询问部分的计算量不会超过\(2e8\),可以轻松通过
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10;
int cnt,qx[N*2],qy[N*2];
int val[6005][6005];
bool vis[6005][6005];
struct pii
{
int x,y;
pii operator =(const pii &b)
{
x=b.x;
y=b.y;
return *this;
}
bool operator <(const pii &b)const
{
if(x!=b.x)return x<b.x;
return y<b.y;
}
bool operator ==(const pii &b)const
{
return (x==b.x)&&(y==b.y);
}
bool operator !=(const pii &b)const
{
return !(*this==b);
}
};
pii make_pair(int x,int y)
{
pii re;
re.x=x;re.y=y;
return re;
}
vector<pii>vec[10000010];
map<int,int> mp;
pii tmp[10];
int uni(pii a[],int n)
{
int k=1;
for(int i=2;i<=n;i++)if(a[i]!=a[k])a[++k]=a[i];
return k;
}
int pre(int n)
{
int m=sqrt(n);
if((m+1)*(m+1)<=n)m=m+1;
for(int i=0;i<=m;i++)
{
int all=n-i*i;
for(int j=i;j*j<=all;j++)
{
int now=i*i+j*j;
int cnt2=0;
tmp[++cnt2]=(make_pair(i,j));
tmp[++cnt2]=(make_pair(-i,j));
tmp[++cnt2]=(make_pair(i,-j));
tmp[++cnt2]=(make_pair(-i,-j));
tmp[++cnt2]=(make_pair(j,i));
tmp[++cnt2]=(make_pair(j,-i));
tmp[++cnt2]=(make_pair(-j,i));
tmp[++cnt2]=(make_pair(-j,-i));
sort(tmp+1,tmp+cnt2+1);
cnt2=uni(tmp,cnt2);
for(int k=1;k<=cnt2;k++)vec[now].push_back(tmp[k]);
}
}
}
bool exist(int x,int y)
{
if(x<1||x>6000||y<1||y>6000)return 0;
return vis[x][y];
}
int main()
{
pre(10000000);
int t;
scanf("%d",&t);
for(int c=1,n,m;c<=t;c++)
{
cnt=0;
printf("Case #%d:\n",c);
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<=n;i++)
{
ll w;
scanf("%d%d%lld",&x,&y,&w);
vis[x][y]=1;
val[x][y]=w;
qx[++cnt]=x;
qy[cnt]=y;
}
ll lst=0;
while(m--)
{
int op,x,y;
int k,w;
scanf("%d%d%d",&op,&x,&y);
x=((x+lst)%6000)+1;
y=((y+lst)%6000)+1;
if(op==1)
{
scanf("%d",&w);
vis[x][y]=1;
val[x][y]=w;
qx[++cnt]=x;
qy[cnt]=y;
}
else if(op==2)
{
vis[x][y]=0;
val[x][y]=0;
}
else if(op==3)
{
scanf("%d%d",&k,&w);
for(int i=0;i<vec[k].size();i++)
{
if(exist(x+vec[k][i].x,y+vec[k][i].y))val[x+vec[k][i].x][y+vec[k][i].y]+=w;
}
}
else
{
scanf("%d",&k);
ll ans=0;
for(int i=0;i<vec[k].size();i++)
{
if(exist(x+vec[k][i].x,y+vec[k][i].y))
{
ans+=val[x+vec[k][i].x][y+vec[k][i].y];
}
}
printf("%lld\n",ans);
lst=ans;
}
}
for(int i=1;i<=cnt;i++)
vis[qx[i]][qy[i]]=val[qx[i]][qy[i]]=0;
cnt=0;
}
}