20190815 T3

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;
}

提示一下大家,计算方法自己推一推就好了,如果不懂在下面留言,我补发图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值