UOJ——【UNR #1】争夺圣杯

1、题意:给一个序列,枚举长度x,然后在这个序列中所有长度为x的区间,我们求出这些区间的最大值之和并取模,最后将所有的异或起来就好啦

2、分析:听说好多人写的O(nlogn) ,特来写一发O(n) 的算法骗访问量

话说这个东西,我们对于每一个点,设这个点的值是max,我们可以求出他影响的所有区间,这个用单调栈解决即可,也就是说求出左边和右边第一个比这个点大的值的位置,设左边那个哪个位置是i,右边那个位置是j,那么我们就能得到这些区间啦,然后我们就可以随便写写就A了 ,这明显是不能AC的,那我们考虑一个点对于每个长度的贡献,考虑这样的一个点pos,令l=pos?i,r=j?pos且l≤r,l≤r我们处理时方便,打出表来(雾),其实能看出来,我们的这个贡献变成了一个分段函数:

①当1≤x≤l时,y=x;

②当l<x≤r+1时,y=l+1;

③当r+1<x≤r+l+1时,y=r+2+l?x。

那么我们分别来计算这三个函数对答案的贡献。

①:这个我们可以找出一个长度x和一个长度x?1的区别,那么所有x 长度有的贡献的位置,在x?1 这个位置也一定拥有,只不过差了一个max(根据那个函数是个等差数列能得出),但是当l=x?1时,这个位置就对于x?1 有贡献,而对x 没有贡献,所以我对于所有的贡献,我在l这个位置加上一个小标记,标记是那个max,那么我从后向前递推,也就是说ansx?1=ansx/x?(x?1)+lazyx?1,其中lazyx?1 表示这个点累计的标记和

②:这个是最水的一个,我们差分一下,然后求一下前缀和就好了QAQ。

③:这个貌似是最复杂的一个,还是打标记这种东西,我们发现这个分段函数单调递减,那么我们在递减开始的r+2 这个位置打一个标记,就是加上max?l,也是差分,那么我们每次都要在一个位置减掉一个max,因为贡献是单调递减的,那么我们还要维护这个差分的数组,另外这个递减最后变成0后就不会再递减了,所以还要开一个差分数组,这段说起来很繁琐,具体看看代码吧

最后友情提示,那个单调栈的判断一定要左边寻找比他大,右边寻找大于等于它的,这样相等的情况才能包括进去

解决啦,撒花!

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 1000010
#define LL long long
#define MOD 998244353

inline int read(){
    char ch = getchar(); int x = 0, f = 1;
    while(ch < ‘0‘ || ch > ‘9‘){
        if(ch == ‘-‘) f = -1;
        ch = getchar();
    }
    while(‘0‘ <= ch && ch <= ‘9‘){
        x = x * 10 + ch - ‘0‘;
        ch = getchar();
    }
    return x * f;
}

int a[M];
int ll[M];
int rr[M];
int z[M], tot;
LL ans[M], beta[M], lazy[M], plus[M], vfk[M];
LL cf[M];

int main(){
    int n = read();
    for(int i = 1; i <= n; i ++) a[i] = read();
    for(int i = 1; i <= n; i ++){
        while(tot > 0 && a[z[tot]] <= a[i]) tot --;
        ll[i] = z[tot]; z[++ tot] = i;
    }
    tot = 0; z[0] = n + 1;
    for(int i = n; i >= 1; i --){
        while(tot > 0 && a[z[tot]] < a[i]) tot --;
        rr[i] = z[tot]; z[++ tot] = i;
    }
    for(int i = 1; i <= n; i ++){
        int l = i - ll[i] - 1;
        int r = rr[i] - i - 1;
        if(l > r) swap(l, r);
        if(l != 0){
            LL w = min(l, r);
            lazy[w] += (LL)w * (LL)a[i];
            beta[r + 2] += (LL)w * (LL)a[i];
            //puts("fuck");
            plus[r + 2] += a[i];
            vfk[r + 2 + l] += a[i];
        }
        cf[l + 1] += (LL)(l + 1) * (LL)a[i];
        cf[r + 2] -= (LL)(l + 1) * (LL)a[i];
    }
    for(int i = n + 1; i > 1; i --){
        ans[i - 1] = ans[i] / (LL)(i) * (LL)(i - 1) + lazy[i - 1];
    }
    LL sum = 0ll, divt = 0ll;
    for(int i = 1; i <= n; i ++){
        sum += beta[i] - divt;
        divt += plus[i];
        divt -= vfk[i];
        ans[i] += sum;
    }
    sum = 0ll;
    for(int i = 1; i <= n; i ++){
        sum += cf[i];
        ans[i] += sum;
    }
    for(int i = 1; i <= n; i ++) ans[i] %= MOD;
    LL res = 0ll;
    for(int i = 1; i <= n; i ++){
        res ^= ans[i];
    }
    printf("%lld\n", res);
    return 0;
}
时间: 2024-08-29 03:46:23

UOJ——【UNR #1】争夺圣杯的相关文章

[UOJ213][UNR #1]争夺圣杯

uoj description 一个长为\(n\)的序列,给定一个参数\(m\),求所有长度为\(m\)的区间的最大值之和. 对于所有的\(m\in[1,n]\)你都需要分别求出答案然后异或起来. \(n\le10^6\) sol 枚举区间长度\(m\)看上去不好做,我们改变一下顺序,枚举每个位置\(i\),考虑它对每个长度的答案的贡献. 设\(L_i\)为\(i\)左边第一个大于等于\(a_i\)的数的出现位置,\(R_i\)为\(i\)右边第一个大于(一定需要有一边不能取等)\(a_i\)的

uoj#213. 【UNR #1】争夺圣杯

http://uoj.ac/problem/209 单调栈求出每个位置x左边第一个大于它的位置L[x]和右第一个不小于它的位置R[x],于是矩形L[x]<=l<=x<=r<=R[x]内的点(l,r)对应的区间[l,r]的最值为x位置的值,这个矩形内的点只对答案数组的二阶差分的四个位置有影响,可以全部统计后再求两次前缀和得到答案. #include<bits/stdc++.h> typedef long long i64; const int N=1e6+7,P=9982

[UOJ UNR#2 UOJ拯救计划]

来自FallDream的博客,未经允许,请勿转载,谢谢. 传送门 感觉这题有点神... 模数是6比较奇怪,考虑计算答案的式子. Ans=$\sum_{i=1}^{k} P(k,i)*ans(i)$ ans(i)表示恰好用i种颜色的方案数. 发现i<=2时候才有贡献 i=1的时候,只有m=0才有贡献,否则没有 i=2的时候,判断图是否是二分图,是的话答案就是2^(联通块个数) #include<iostream> #include<cstring> #include<cs

UNR #1 题解

虽然题解讲的很清楚...但还是再写一遍骗一点访问量QAQ A. 争夺圣杯 还是想说一下,这题是原题啊...想做的人可以戳codechef上的MTMXSUM(懒得贴链接了,套了个壳,不过正常人应该都能看得出来) 显然异或输出没什么奇怪的性质... 考虑一个元素a[x]在哪些区间中会成为最大值,我们可以用单调栈找出前面比这个元素大的第一个元素a[l],右边大的第一个元素a[r]. 考虑这个元素对每一长度的贡献,设p=x-l,q=r-x,那么对于区间[s,t],只有当l<s<=x,x<=t&l

UOJ #390. 【UNR #3】百鸽笼

UOJ #390. [UNR #3]百鸽笼 题目链接 看这道题之前先看一道相似的题目 [PKUWC2018]猎人杀. 考虑类似的容斥: 我们不妨设处理\(1\)的概率. 我们另集合\(T\)中的所有鸽笼都在\(1\)变空之前不为空的,其它的鸽笼随便.要做到这一点,我们只需要令每个\(T\)集合中的鸽笼容量\(--\)就行了.然后我们用背包背出所有序列的方案数(不包括\(1\)),然后在将\(1\)插入序列中.插入时,将\(w_i-1\)个随便插入,然后再将一个放在序列末尾. 具体实现时,我们可以

【UOJ#386】【UNR#3】鸽子固定器(贪心)

[UOJ#386][UNR#3]鸽子固定器(贪心) 题面 UOJ 题解 一个不难想到的暴力做法是把东西按照\(s\)排序,这样子我们枚举极大值和极小值,那么我们选择的一定是这一段之间\(v\)最大的那\(m\)个东西. 考虑优化这个过程,我们枚举右端点,左端点向左移动,每次插入一个元素,用堆来维护选择的过程.这样子复杂度可以做到\(O(n^2logn)\). 考虑继续优化这个过程,首先如果右端点一旦被弹出堆这个过程就可以终止了,这个很显然. 通过这个过程,我们也可以明白如果选择的个数不超过\(m

【UOJ#308】【UNR#2】UOJ拯救计划

[UOJ#308][UNR#2]UOJ拯救计划 题面 UOJ 题解 如果模数很奇怪,我们可以插值一下,设\(f[i]\)表示用了\(i\)种颜色的方案数. 然而模\(6\)这个东西很有意思,\(6=2*3\),所以我们只需要考虑其模\(2\)和模\(3\)的结果了. 而最终答案的贡献是\(\sum_{i=1}^k A_{k}^i f[i]\),当\(i\ge 3\)的时候\(6|A_k^i\),所以我们只需要知道\(f[0],f[1],f[2]\)的值. \(f[0]\)的值?当然是\(0\)啊

uoj308 【UNR #2】UOJ拯救计划

传送门:http://uoj.ac/problem/308 [题解] 考虑枚举用了$i$所学校,那么贡献为${k \choose i} * cnt * i!$ 意思是从$k$所选$i$所出来染色,$cnt$为固定颜色顺序的染色方案,$i!$为可以交换学校位置. 考虑当$i \geq 3$的时候,贡献含有模数因子6,所以模6为0,相当于没有贡献. 当$i = 1$,显然只有$m = 0$有贡献. 对于$m = 0$我们特判,答案显然是$K^n$. 剩下$i = 2$的情况,也就是我们要判断答案是不

UOJ #218. 【UNR #1】火车管理

http://uoj.ac/problem/218 维护一颗主席树 查询入栈相当于区间修改,弹栈相当于返回历史版本 维护区间求和 原文地址:https://www.cnblogs.com/sssy/p/8322124.html