20180815 T3
题目
三角形(triangle.c/cpp/pas)
【题目描述】
平面上有n行m列,一共n*m个方格,从上到下依次标记为第1,2,…,n行,从左到右依次标记为第1,2,…,m列,方便起见,我们称第i行第j列的方格为(i,j)。小Q在方格中填满了数字,每个格子中都恰好有一个整数a_{i,j}。小Q不喜欢手算,因此每当他不想计算时,他就会让你帮忙计算。小Q一共会给出q个询问,每次给定一个方格(x,y)和一个整数k(1<=k<=min(x,y)),你需要回答由(x,y),(x-k+1,y),(x,y-k+1)三个格子构成的三角形边上以及内部的所有格子的a的和。
【输入格式】
第一行包含6个正整数n,m,q,A,B,C(1<=n,m<=3000,1<=q<=3000000,1<=A,B,C<=1000000)
其中n,m表示方格纸的尺寸,q表示询问个数。
为了防止输入数据过大,a和询问将由以下代码生成:
unsigned int A,B,C;
inline unsigned int rng61(){
A ^= A << 16;
A ^= A >> 5;
A ^= A << 1;
unsigned int t = A;
A = B;
B = C;
C ^= t ^ A;
return C;
}
int main(){
scanf("%d%d%d%u%u%u", &n, &m, &q, &A, &B, &C);
for(i = 1; i<= n; i++)
for(j = 1; j <= m; j++)
a[i][j] = rng61();
for(i = 1; i<= q; i++){
x = rng61() % n + 1;
y = rng61() % m + 1;
k = rng61() % min(x, y) + 1;
}
}
【输出格式】
为了防止输出数据过大,设f_i表示第i个询问的答案,则你需要输出一行一个整数,即:
输入样例:
3 4 5 2 3 7
输出样例
3350931807
赛况
3人ac,其中又有freopen,作者本人并没有选择做这个题,下来发现挺简单的
题解
可以用uint 自然溢出
重点(搞懂这个这个题就比较好推了)
a[i][j] 表示从原点(1,1)到(i,j)这个矩阵中的所有元素和
b[i][j]表示第2排的前缀和
c[i][j]表示三角形中的元素和(也可称之为梯步型)如图所示
图中蓝色部分即为c(5,1);
预处理部分
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
a[i][j]=rng61();
b[i][j]+=b[i][j-1]+a[i][j];
c[i][j]=b[i][j]+c[i-1][j+1];
a[i][j]=b[i][j]+a[i-1][j];
}
c[i][0]=c[i-1][1];
}
然后自己加加减减就可以求出了最终求出的三角形,下面标程自行阅读
老师的标程
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int mod=1e9+7;
const int maxn=3e3+100;
const double eps=1e-10;
unsigned int A,B,C;
int n,m,q,x,y,k;
unsigned int a[maxn][maxn],b[maxn][maxn],c[maxn][maxn],d[3000000+100];
inline unsigned int rng61(){
A^=A<<16;
A^=A>>5;
A^=A<<1;
unsigned int t=A;
A=B;
B=C;
C^=t^A;
return C;
}
int main()
{
//freopen("triangle.in","r",stdin);
//freopen("triangle.out","w",stdout);
d[0]=1;
for(int i=1;i<=3000000;++i)
d[i]=d[i-1]*233;
scanf("%d%d%d%u%u%u",&n,&m,&q,&A,&B,&C);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
a[i][j]=rng61();
b[i][j]+=b[i][j-1]+a[i][j];
c[i][j]=b[i][j]+c[i-1][j+1];
a[i][j]=b[i][j]+a[i-1][j];
}
c[i][0]=c[i-1][1];
}
unsigned int ans=0;
for(int i=1;i<=q;i++)
{
x=rng61()%n+1;
y=rng61()%m+1;
k=rng61()%min(x,y)+1;
int x1=x-k,y1=y-k;
if(x1<0)x1=0;
if(y1<0)y1=0;
unsigned tmp1=c[x][y1]-c[x1][y],tmp2=a[x][y1]-a[x1][y1],tmp3=tmp1-tmp2;
unsigned tmp4=a[x][y]-a[x1][y]-a[x][y1]+a[x1][y1],tmp5=tmp4-tmp3;
ans+=d[q-i]*tmp5;
}
printf("%u\n",ans);
return 0;
}
改进方案
其实我们发现,老师给的标程中的x1,y1其实不会小于0的,所以最终这两条语句没有用。
而且我们最后求得时候方法可以改进
uint tmp2=c[x][y1]-c[x1][y],
tmp3=a[x1][y],
tmp5=a[x][y]-tmp2-tmp3;
此时我们就可以省下一个b数组
自己的标程
#include <cstdio>
#include <iostream>
using namespace std;
const int maxq = 3e6+5;
const int maxn = 3e3+5;
#define uint unsigned int
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
uint A,B,C;
int x,y,k;
int n,m,q;
uint a[maxn][maxn];
uint b[maxn][maxn];
uint c[maxn][maxn];
uint fac[maxq+100];
inline uint rand61(){
A^=A<<16;
A^=A>>5;
A^=A<<1;
uint t=A;
A=B;
B=C;
C^=t^A;
return C;
}
int main (){
scanf("%d %d %d %u %u %u",&n,&m,&q,&A,&B,&C);
fac[0]=1;
for(int i=1;i<=3000000;++i)
fac[i]=fac[i-1]*233;
uint b1=0,b=0;
for(int i=1;i<=n;i++){
b=0;
for(int j=1;j<=m;j++){
a[i][j]=rand61();
b=b+a[i][j];
c[i][j]=b+c[i-1][j+1];
a[i][j]=b+a[i-1][j];
}
c[i][0]=c[i-1][1];
}
uint ans=0;
for(int i=1;i<=q;i++){
x=rand61()%n+1;
y=rand61()%m+1;
k=rand61()%min(x,y)+1;
int x1=x-k,y1=y-k;
uint tmp2=c[x][y1]-c[x1][y],
tmp3=a[x1][y],
tmp5=a[x][y]-tmp2-tmp3;
ans+=fac[q-i]*tmp5;
}
cout<<ans;
return 0;
}