张三 (fwkt)(二阶差分)

时间限制: 1 Sec 内存限制: 128 MB
题目描述
听说罗翔老师最近很火。
张三今天迫不及待想犯罪。就决定是爆炸罪了,正好罗老师讲了这个案例。
他一眼就看上了 CaPeF_Yyx 的家 (或许是他太 duliu 了) 。
作为张三,她蛮不讲理,所以他的爆炸会波及部分街道。每次爆炸会造成一定的损坏。
作为张三,她出人意料,所以他的爆炸波及的范围每次都不一样。
作为张三,她十恶不赦,所以他的爆炸会进行m 次 (CaPeF_Yyx 的家也太坚固了吧)
作为张三,她井井有条,所以他的爆炸波及的范围是一个闭区间。
作为张三,她精益求精,所以他的爆炸以等差数列的方式造成伤害。
CaPeF_Yyx 家在偏远角落,街道编号从1~n 。
CaPeF_Yyx 街道刚翻新,每间房屋破坏程度都为0。
罗翔老师可以预测每次爆炸的范围。会给到 CaPeF_Yyx 形如[l,r] 的闭区间。
罗翔老师可以预测每次爆炸的伤害。会给到 CaPeF_Yyx 形如l,r,bg,ed 的 4 个数。
表示形如{ql,ql+1,…,qr-1,qr},ql=bg,qr=ed的等差数列,对于街道ai(l≤i≤r),造成qi的伤害。
CaPeF_Yyx 找到了 Rainy7 修复街道。可是她必须先知道街道到底损坏了多少。
她还忙着拯救世界呢,CaPeF_Yyx 被吓得不轻,于是只能让你帮助她了。
出题人不想太 duliu ,她让你只要输出所有房屋损害程度的最大值和异或和。
Q:话说为什么不直接用魔法阻止张三?
A:因为 唯我法外狂徒张三永世长存 !!!
输入
第一行,两个整数n,m表示房屋的数量,和爆炸次数。

接下来m行,每行四个整数l,r,bg,ed,分别表示等差数列的首项标号,末项标号,首项值,末项值。
输出
一行,两个数,分别表示所有房屋损害程度的最大值和异或和。
样例输入 Copy
【样例1】

5 2
1 5 2 10
2 4 1 1

【样例2】

6 2
1 5 2 10
2 4 1 1

样例输出 Copy
【样例1】

3 10

【样例2】

3 10

提示
样例1解释:
第一次爆炸产生的伤害:[2,4,6,8,10]。
第二次爆炸产生的伤害:[0,1,1,1,0]。
所有爆炸结束后每个房屋的损伤程度:[2,5,7,9,10]。
输出异或和: 2 ⊕ 5 ⊕ 7 ⊕ 9 ⊕ 10 = 3 2 \oplus 5 \oplus 7 \oplus 9 \oplus 10=3 257910=3
输出最大值: max ⁡ i = 1 n a i = 10 \max _{i=1}^{n} a_{i}=10 maxi=1nai=10
对于全部数据: 1 ≤ n ≤ 1 0 7 , 1 ≤ m ≤ 3 × 1 0 5 , 1 ≤ l < r ≤ n 1 \leq n \leq 10^{7}, 1 \leq m \leq 3 \times 10^{5}, 1 \leq l<r \leq n 1n107,1m3×105,1l<rn
保证bg,ed非负。
保证所有房屋的损坏程度保持在 [ 0 , 9 × 1 0 18 ] \left[0,9 \times 10^{18}\right] [0,9×1018]
Subtask #1 ( 20  points ) : 1 ≤ n , m ≤ 1 0 3 (20 \text { points}): 1 \leq n, m \leq 10^{3} (20 points):1n,m103
Subtask #2 ( 10  points ) : b g = e d (10 \text { points}): b g=e d (10 points):bg=ed
Subtask #3 ( 30  points ) : 1 ≤ n , m ≤ 1 0 5 (30 \text { points}): 1 \leq n, m \leq 10^{5} (30 points):1n,m105
Subtask #4 ( 40  points ) : (40 \text { points}) : (40 points): 无特殊性质。
考查二阶差分+前缀和。
由等差数列的性质可知等差数列作差分后得到一个序列,该序列的绝大部分元素相同,然后考虑再作一次差分,序列中将会出现大部分0,由此联想到利用差分序列端点修改实现区间修改。
下面尝试寻找规律:
举个例子,对于序列
在这里插入图片描述
现对区间[l,r](l=3,r=6)加上首项为a2,公差为d2的等差数列,得到新的序列
在这里插入图片描述
对比改变前后,结合改变的区间[3,6]可以发现只需单点修改l,l+1,r+1,r+2。

序列[l]+=首项;
序列[l+1]+=公差-首项;
序列[r+1]-=首项+(r-l+1)*公差;//注意r+1处要减,其余的均为加。
序列[r+2]+=首项+(r-l)*公差;

我们只需维护区间[1,n]的前缀和,可以验证此结论对任何两个改变前后的序列都成立。

#include<cstdio>
#include<algorithm>
using namespace std;
long long int street[10000005]={
    
    0};
int main()
{
    
    
    int n,m;
    long long int ans=0,mmax=-1,bg,ed,l,r,delta;//此题的数据量较大,为避免溢出,要把中间的量以及结果设为long long型。
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
    
    
        scanf("%lld %lld %lld %lld",&l,&r,&bg,&ed);
        delta=(ed-bg)/(r-l);
        street[l]+=bg;
        street[l+1]+=delta-bg;
        street[r+1]-=bg+(r-l+1)*delta;
        street[r+2]+=bg+(r-l)*delta;
    }
    for(int i=1;i<=n;i++)
    {
    
    
        street[i]+=street[i-1];
    }
    for(int i=1;i<=n;i++)//两次前缀和
    {
    
    
        street[i]+=street[i-1];
        ans=ans^street[i];
        mmax=max(mmax,street[i]);
    }
    printf("%lld %lld",ans,mmax);
    return 0;
}
/**************************************************************
    Language: C++
    Result: 正确
    Time:387 ms
    Memory:79244 kb
****************************************************************/

猜你喜欢

转载自blog.csdn.net/upc122/article/details/106676427
今日推荐