P4231 三步必杀——二阶差分

问题摘要:

\(N\)个柱子排成一排,一开始每个柱子损伤度为0。

接下来勇仪会进行\(M\)次攻击,每次攻击可以用4个参数\(l\),\(r\),\(s\),\(e\)来描述:

表示这次攻击作用范围为第\(l\)个到第\(r\)个之间所有的柱子(包含\(l\),\(r\)),对第一个柱子的伤害为\(s\),对最后一个柱子的伤害为\(e\)

攻击产生的伤害值是一个等差数列。若\(l=1\),\(r=5\),\(s=2\),\(e=10\),则对第1~5个柱子分别产生2,4,6,8,10的伤害。

鬼族们需要的是所有攻击完成之后每个柱子的损伤度。

输入格式

第一行2个整数\(N\),\(M\),用空格隔开,下同。

接下来\(M\)行,每行4个整数\(l\),\(r\),\(s\),\(e\),含义见题目描述。

数据保证对每个柱子产生的每次伤害值都是整数。

输出格式

由于输出数据可能过大无法全部输出,为了确保你真的能维护所有柱子的损伤度,只要输出它们的异或和与最大值即可。

(异或和就是所有数字按位异或起来的值)

(异或运算符在c++里为^)

样例 #1

样例输入 #1

5 2
1 5 2 10
2 4 1 1

样例输出 #1

3 10

样例 #2

样例输入 #2

6 2
1 5 2 10
2 4 1 1

样例输出 #2

3 10

提示

样例解释:

样例1:

第一次攻击产生的伤害:2 4 6 8 10

第二次攻击产生的伤害:0 1 1 1 0

所有攻击结束后每个柱子的损伤程度:2 5 7 9 10。

输出异或和与最大值,就是3 10。

样例2:

没有打到第六根柱子,答案不变

数据范围:

对于全部的数据:\(1\leqslant n\leqslant10^7\),\(1\leqslant m\leqslant3\times 10^5\)\(1\leqslant l<r\leqslant n\).

所有输入输出数据以及柱子受损伤程度始终在\([0,9\times 10^{18}]\)范围内。


等差数列,公差相同。对一个序列的一段加上一个等差数列。

想到差分(我没想到),设 \(a\) 为原数列, \(b\) 为一阶差分数列,\(c\) 为二阶差分数列。

差分是探究数列关系的利器,如果一阶差分不能解决问题,那就再差一阶。另外 多项式也可以差分。

定更改的区间为 \([L,R]\),首项为 \(s\),公差是 \(d\),末项是 \(t = s + (R - L)\times d\)

看对原数列 \(a\) 的影响:\(a_x=a_x + s + (x - L)\times d\)

作一阶差分,看 \(b\) 的影响:\(b_x=a_{x}-a_{x-1}\)

\(b_L = b_L + s\)

\(b_{x}=b_x+d\)

\(b_{R+1}=b_{R+1}-t\)

此时更改次数仍是 \(R-L\) 次,但是已经初见端倪。再看 \(c\) 的变化:\(c_x=b_x-b_{x-1}\)

\(c_L=c_L+s\)

\(c_{L+1}=c_{L+1}+d-s\)

\(c_{x}=c_x\)

\(c_{R+1}=C_{R+1}-t-d\)

\(c_{R+2}=c_{R+2}+t\)

此时找到 \(O(1)\) 算法,每次覆盖只需要改变四个点

#include <bits/stdc++.h>
#define rei register int
#define ll long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define rep(i, s, n, c) for (register int i = s; i <= n; i+=c)
#define repd(i, s, n, c) for (register int i = s; i >= n; i-=c)
#define CHECK cout<<"WALKED"<<endl;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
#define pb push_back
#define ls id<<1
#define rs id<<1|1
const int INF = INT_MAX;
long long binpow(long long a, long long b, ll mod){long long res = 1;  while (b > 0){if (b & 1) res = res * a % mod;a = a * a % mod;  b >>= 1;  }  return res;}

using namespace std;

const int maxn = 10000005;

ll a[maxn], b[maxn], c[maxn], maxx, sum;
int n, m;
int l, r, s, e, d;

int main()
{
	n = read(); m = read();
	for (int i = 1; i <= m; i++) {
		l = read(), r = read(), s = read(), e = read();
		d = (e - s) / (r - l); // get common difference
		
		// 4 places
		c[l] = c[l] + s;
		c[l + 1] = c[l + 1] + d - s;
		c[r + 1] = c[r + 1] - d - e;
		c[r + 2] = c[r + 2] + e;
	}
	for (int i = 1; i <= n; i++) {
		b[i] = b[i - 1] + c[i]; // second diff
		a[i] = a[i - 1] + b[i]; // first diff
	}
	for (int i = 1; i <= n; i++) {
		maxx = max(a[i], maxx);
		sum ^= a[i];
	}
	cout << sum << " " << maxx << endl;
    return 0;
}
posted @ 2022-12-22 08:08  Vegdie  阅读(66)  评论(0编辑  收藏  举报