編程訓練_求和公式

問題描述:函數

QQ小方之前不會用 excel 裏的求和公式,如今他會了,因此他急切的想教會你。
對於 excel 中的一個子矩陣求和的公式格式是 sum(A:B) ,其中 A 表示子矩陣的左上角座標, B 表示子矩陣的右下角座標。

  

單單講給你聽確定是不夠的,爲了表現本身,QQ小方如今要考考你。
如今QQ小方會給你一個 n×n 的正方形矩陣,而且它會將正方形矩陣的兩條對角線位置所有填上 1 (能夠參考下圖),其他位置所有填上 0 。而且他給正方形矩陣的行和列從 0 到 n−1 標上了序號。
例以下圖所示是 4×4 正方形矩陣的填充和行列標號:

  

如今,爲了測試你是否真的掌握了 excel 的矩陣求和公式,QQ小方會進行 q 次詢問,每次詢問一個子矩陣的和。
輸入格式
輸入第一行包含兩個整數 n,q(1≤n≤10^18,1≤q≤10^5) ,分別表示矩陣的大小和詢問的次數。
接下來的 q 行,每行包含四個整數 a,b,c,d(0≤a≤c<n,0≤b≤d<n) ,表示子矩陣。其中子矩陣的左上角座標爲 (a,b) ,右下角座標爲 (c,d) 。
輸出格式
輸出包含 q 行,針對每個詢問輸出一個整數表示答案。

樣例
input
4 2
0 1 1 3
0 0 3 3
output
3
8

題目求解:測試

1.題目大意轉化爲一個矩形包含離散直線 y=x 和 y=n-x-1 點的個數問題。spa

2.值得注意的是包含的點數目可能爲0,且矩陣的範圍是10^18,若是使用循環遍歷的話,極可能會超時。excel

3.下面對直線y=x 求包含點的個數,即在X軸上的投影,經過繪圖能夠得出以下結論blog

(1). 與 y=x 相交的點有 (a,a),(b,b),(c,c),(d,d),並且落在矩形邊界的點在X軸上投影爲 max(a,b),min(c,d);input

(2). 與 y=x 無交點的判別式:(b-c)*(d-a)>0io

   分析:若矩形與直線 y=x 無交點,則矩形處在直線的上方或者下方,即 (b-c)與(d-a)同號,即(b-c)*(d-a)>0;class

(3). 對於直線 y=x 求交點個數循環

// 當在直線 y=x 異側時有交點
if ((b-c)*(d-a)<=0)
    sum_i = sum(max(a,b),min(c,d));
// 其中sum(int m, int n) 函數的計算方式爲
//  abs(n-m)+1;

4.下面對直線y=n-x-1 求包含點的個數,方法同3遍歷

(1). 與 y=n-x-1 相交的點有 (a,n-1-a),(b,n-1-b),(n-1-c,c),(d,n-1-d),並且落在矩形邊界的點在X軸上投影爲 max(b,n-1-c),min(n-1-a,d);

(2). 與 y=n-x-1 無交點的判別式:(a+b+1-n)*(c+d+1-n)>0

   分析:若矩形與直線 y=n-x-1 無交點,則矩形處在直線的上方或者下方,即 (a+b+1-n)與(c+d+1-n)同號,即(a+b+1-n)*(c+d+1-n)>0;

(3). 對於直線 y=n-x-1 求交點個數

// 當在直線 y=n-x-1 異側時有交點
if ((a+b+1-n)*(c+d+1-n)<=0)
    sum_j = sum(max(b,n-1-c),min(d,n-1-a));

5. 討論兩條直線存在重複值的狀況

(1). 因爲該直線表達形式爲連續直線,則當n爲奇數時,會存在中心交點重合的狀況;

(2). 此狀況下該點有可能計算兩次,應該去重

if (n%2 == 1)
    if (a<=n/2 && c>=n/2 && b<=n/2 && d>=n/2)
        flag = 1;	

問題反思:

1.該矩陣的階次達到 10^18,儘可能避免循環遍歷;

2.判斷矩形是否與直線相交時,應避免整數相乘再判斷符號;

// 1. y=x
// 最好不要採用乘積爲非正的算式,當數很大的時候會報錯 
if (b>=c&&d<=a || b<=c&&d>=a)
	sum_i = sum(max(a,b),min(c,d));
// 2. y=n-x+1 
if (b>=(n-1-a)&&d<=(n-1-c) || b<=(n-1-a)&&d>=(n-1-c))
	sum_j = sum(max(b,n-1-c),min(n-1-a,d));

3.使用求和函數時爲了保證結果爲正,也應避免使用abs()函數,由於精度會下降,從而致使報錯;

long long sum(long long a, long long b)
{
	// printf("abs sum = %lld\n",abs(a-b+1));	
	// printf("sum = %lld\n",(a>b?a-b+1:b-a+1));
       // 上述兩式的結果不一樣
	return a>b?a-b+1:b-a+1;
}

4.不要忘記n爲奇數時的去重處理。

if (n%2 == 1)
	if (a<=n/2 && c>=n/2 && b<=n/2 && d>=n/2)
		flag = 1;			
printf("%lld\n",sum_i+sum_j-flag);

AC代碼:

# include <stdio.h>
# include <math.h>

long long max(long long a, long long b)
{
	return a>b?a:b;
}

long long min(long long a, long long b)
{
	return a>b?b:a;
}

long long sum(long long a, long long b)
{
	return a>b?a-b+1:b-a+1;
}

int main(void)
{
	long long n, sum_i, sum_j, flag = 0;
	int q;
	long long a, b, c, d;
	scanf("%lld %d",&n, &q); 
	
	while (q>0)
	{
		flag = 0;
		sum_i = sum_j = 0;
		scanf("%lld %lld %lld %lld",&a,&b,&c,&d);
		// 分兩條線計算,求交點座標,而後計算包含的點個數 
		// 1. y=x
		// 最好不要採用乘積爲非正的算式,當數很大的時候會報錯 
		if (b>=c&&d<=a || b<=c&&d>=a)
			sum_i = sum(max(a,b),min(c,d));
		// 2. y=n-x+1 
		if (b>=(n-1-a)&&d<=(n-1-c) || b<=(n-1-a)&&d>=(n-1-c))
			sum_j = sum(max(b,n-1-c),min(n-1-a,d));
		q--;
		if (n%2 == 1)
			if (a<=n/2 && c>=n/2 && b<=n/2 && d>=n/2)
				flag = 1;			
		printf("%lld\n",sum_i+sum_j-flag);
	}
	
	return 0;
 } 

RRR

相關文章
相關標籤/搜索