• JZOJ 6944. 【2020.01.07冬令营模拟】球(数学计算)


    JZOJ 6944. 【2020.01.07冬令营模拟】球

    题解

    • 发现数据很良心,区间坐标都是非负数,避免了更多的分类讨论。
    • 把区间拆开考虑, 发现在对角线 y = x y=x y=x两侧都满足每行/列分别单调,如果在某一侧框出一个矩形,可以用等差数列求和直接解决。
    • 同时对角线上的可以用平方和计算,但对角边旁构不成矩形的部分如何解决?
    • 又发现可以把对角线所经过部分框出一个正方形,分层后每一层都可以用一个最高次为三次的多项式求和计算,直接用立方和公式和平方和公式,那么这样剩下的部分都分居对角线两侧,用上述的方法。
    • 至于模数,看似不能用自然溢出,其实并不然,因为 2 64 2^{64} 264 2 63 2^{63} 263的倍数,所以可以自然溢出完再取模一次。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define md 9223372036854775808
    ll read() {
    	ll s = 0;
    	char x = getchar();
    	while(x < '0' || x > '9') x = getchar();
    	while(x >= '0' && x <= '9') s = s * 10 + x - 48, x = getchar();
    	return s;
    }
    void write(ll x) {
    	if(x < 0) x += md;
    	if(x >= md) x -= md;
    	int c[25], l = 0;
    	if(x == 0) puts("0");
    	else {
    		while(x) c[++l] = x % 10, x /= 10;
    		while(l) putchar(c[l] + 48), l--;
    		puts("");
    	}
    }
    ll sq(ll x) {
    	ll t = x + 1, s = 2 * x + 1;
    	if(x % 2 == 0) x /= 2; else if(t % 2 == 0) t /= 2; else s /= 2;
    	if(x % 3 == 0) x /= 3; else if(t % 3 == 0) t /= 3; else s /= 3;
    	return x * s * t;
    }
    ll sum(ll fi, ll d, ll n) {
    	ll la = fi + d * (n - 1);
    	if(n % 2 == 0) return (fi + la) * (n / 2);
    	return (fi + la) / 2 * n;
    }
    ll ct(ll x) {
    	if(x < 0) return 0;
    	return sq(x) - sq(x / 2) * 4;
    }
    ll count(ll x, ll y) {
    	return ct(y) - ct(x - 2);
    }
    ll squ(ll x) {
    	if(x == 0) return 1;
    	ll s = sum(1, 1, (x * 2 + 1) * (x * 2 + 1));
    	ll r = sum(1, 1, x);
    	ll t = r * r * 24 + r * 6 - sq(x) * 4 - x;
    	return s - t;
    }
    ll solve(ll lx, ll rx, ll ly, ll ry) {
    	if(lx > ry)  {
    		return sum(count(lx * 2 - 1, rx * 2 - 1) + sum(lx - ry, 1, rx - lx + 1), rx - lx + 1, ry - ly + 1);
    	}
    	if(rx < ly) {
    		return sum(count(ly * 2 + 1, ry * 2 + 1) - sum(ly - rx, 1, ry - ly + 1), -(ry - ly + 1), rx - lx + 1);
    	}
    	if(lx == ly) {
    		if(rx == ry) {
    			if(lx == 0) return squ(rx);
    			return squ(rx) - squ(lx - 1) - solve(lx, rx, 0, ly - 1) - solve(0, lx - 1, ly, ry);
    		}
    		else if(rx < ry) {
    			return solve(lx, rx, ly, rx) + solve(lx, rx, rx + 1, ry);
    		}
    		else if(ry < rx) {
    			return solve(lx, ry, ly, ry) + solve(ry + 1, rx, ly, ry);
    		}
    	}
    	else if(ly > lx) {
    		if(rx == ry) {
    			return solve(ly, rx, ly, ry) + solve(lx, ly - 1, ly, ry);
    		}
    		else if(rx < ry) {
    			return solve(ly, rx, ly, rx) + solve(lx, ly - 1, ly, ry) + solve(ly, rx, rx + 1, ry);
    		}
    		else if(ry < rx) {
    			return solve(ly, ry, ly, ry) + solve(lx, ly - 1, ly, ry) + solve(ry + 1, rx, ly, ry);
    		}
    	}
    	else if(lx > ly) {
    		if(rx == ry) {
    			return solve(lx, rx, lx, ry) + solve(lx, rx, ly, lx - 1);
    		}
    		else if(rx < ry) {
    			return solve(lx, rx, lx, rx) + solve(lx, rx, ly, lx - 1) + solve(lx, rx, rx + 1, ry);
    		}
    		else if(ry < rx) {
    			return solve(lx, ry, lx, ry) + solve(lx, rx, ly, lx - 1) + solve(ry + 1, rx, lx, ry);
    		}
    	}
    }
    int main() {
    	int Q = read();
    	while(Q--) {
    		ll lx = read(), rx = read(), ly = read(), ry = read();
    		write(solve(lx, rx, ly, ry));
    	}
    	return 0;
    }
    

    自我小结

    • 比赛时想到了正解,但是因为一个字母写错和读入时没有开long long而爆零。
    • 值得注意,写代码时避免惯性思维,因“手熟”而写错。
    • 另外,内容比较对称的代码, 也可以对着检查,有错误容易查出。
  • 相关阅读:
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1057:简单计算器
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1061:求整数的和与均值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1061:求整数的和与均值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1061:求整数的和与均值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1060:均值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1060:均值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1060:均值
    如何绕过Windows Server 2008 R2上的身份验证
    如何绕过Windows Server 2008 R2上的身份验证
    SQLServer中创建主键的方法
  • 原文地址:https://www.cnblogs.com/LZA119/p/14279478.html
Copyright © 2020-2023  润新知