极端不擅长DP……orz
题意大概是这样的:
在一个平面直角坐标系的第一象限中有k个整点带有权值,且权值不超过c。ts后由于发生了一些事情,权值变成了k+t,当然如果超过c就重新计算。
举个栗子:
3 4 5
1 1 2
2 3 0
3 3 1
0 1 1 100 100
1 2 2 4 4
2 2 1 4 7
1 50 50 51 51
第一行表示要输入3个点,4次查询,最大权值为5
之后的三行,表示(1,1)权值2,(2,3)权值0,(3,3)权值1
之后第一次查询,(1,1)到(100,100)权值和3,第二次查询为1s后,(3,3)查询在范围内权值变为2,(2,3)变1,则结果为3.同理,之后查询,结果分别为5、0
数据范围:点数、 查询数1 ≤ n,q ≤
105
,权值上限 1 ≤ c ≤ 10
每个点
1 ≤ xi, yi ≤ 100, 权值0 ≤ si ≤ c ≤ 10
每次查询
0 ≤ ti ≤
109
, 1 ≤
x1i
<
x2i
≤ 100, 1 ≤
y1i
<
y2i
≤ 100
我们的一个直观想法就是先开一个二维数组来存数然后每次查询遍历数组以求出前缀和。这样的每次查询都是
O(n2)
的,q次查询就是
qn2
,这个复杂度是绝对吃不消的。所以我们需要提前预处理以起到最快的效果。而方法就是 二维前缀和
这玩意说实话我没有听说过。但首先各位请理解一下这个转移方程:
dp[k][i][j]=dp[k][i-1][j]+dp[k][i][j-1]-dp[k][i-1][j-1]+ kx,y
这样我们可以预处理出一个前缀和:即权值为c的点在(x,y)范围内是怎样的前缀和。或者我们也可以这样考虑:将权值作为新的一维来考虑,将每一个权值所表示的平面的每一点的权值和预处理出来。
我们要求的就相当于蓝色矩形的面积+黄色矩形的面积-绿色矩形的面积+该点的权值。
下一步就是怎么求。很容易的我们列出这样一个式子:
query(t,x1,y1,x2,y2)=∑kp=0((p+ti)%(c+1))∗(dp[k][x2][y2]−dp[k][x1−1][y2]−dp[k][x2][y1−1]+dp[k][x1−1][y1−1])
这样每次查询为O(cn)的,预处理则是
O(c×x2)
的
当然这道题挖了一个大坑:点是可以重合的,此时权值叠加取模。嗯……很坑
具体就看代码吧
#include <bits/stdc++.h>
using namespace std;
int dp[11][103][103];
int c,ans,n,q;
int query(int t,int x1,int y1,int x2,int y2){ //查询操作
ans=0;
int e;
for(int i=0;i<=c;++i){
e=(t+i)%(c+1);
ans+=e*(dp[i][x2][y2]-dp[i][x1-1][y2]-dp[i][x2][y1-1]+dp[i][x1-1][y1-1]);
}
return ans;
}
void init(){ //输入并初始化dp数组
cin>>n>>q>>c;
int x1,y1,k;
for(int i=0;i<n;++i){
cin>>x1>>y1>>k;
dp[k][x1][y1]++;
}
for(int i=0;i<=c;++i)
for(int j=1;j<=101;++j)
for(int p=1;p<=101;++p)
dp[i][j][p]+=dp[i][j-1][p]+dp[i][j][p-1]-dp[i][j-1][p-1];
}
int main(){
init();
int t,x1,y1,x2,y2;
while(q--){ //执行
cin>>t>>x1>>y1>>x2>>y2;
cout<<query(t,x1,y1,x2,y2)<<"\n";
}
return 0;
}
我真是太菜了.jpg