20210712考试-2021noip11

这篇总结比我写的好多了建议直接去看


T1 简单的序列

考场:
愣了一会,想到以最大值分治。每次枚举最大值两侧更小的区间,st表预处理前缀和和最大值,用桶统计答案.

注意分治时要去掉最大值。

const int N = 3e5+5, X = 1e6+5;
int n,k,a[N];

int m,s[N],head[N],nxt[N*36],val[N*36],op[N*36],cnt[X];
LL ans;

namespace st {
int lg[N],pw[19],f[19][N],p[19][N];
void init() {
    lg[1] = 0;
    For(i,2,n) lg[i] = lg[i>>1]+1;
    pw[0] = 1;
    For(i,1,18) pw[i] = pw[i-1]<<1;
    For(i,1,n) f[0][i] = a[i], p[0][i] = i;
    For(j,1,18) for(int i = 1; i+pw[j]-1 <= n; ++i) {
        int mid = i+pw[j-1];
        if( f[j-1][i] > f[j-1][mid] )
            f[j][i] = f[j-1][i], p[j][i] = p[j-1][i];
        else f[j][i] = f[j-1][mid], p[j][i] = p[j-1][mid];
    }
}
int maxx(int l,int r) {
    int k = lg[r-l+1], mid = r-pw[k]+1;
    return f[k][l]>f[k][mid] ? p[k][l] : p[k][mid];
}
}

void add(int x,int l,int r) {
    x %= k;
    if( x < 0 ) x += k;
//    printf(">%d %d %d
",x,l,r);
    if( l ) val[++m] = x, op[m] = -1, nxt[m] = head[l-1], head[l-1] = m;
    val[++m] = x, op[m] = 1, nxt[m] = head[r], head[r] = m;
}
void solve(int l,int r) {
    if( l >= r ) { ans += l==r; return; }
    int p = st::maxx(l,r);
//    printf(">%d %d %d
",l,p,r);
    if( p-l < r-p ) For(i,l,p) add(s[i-1]+a[p],p,r);
    else For(i,p,r) add(s[i]-a[p],l-1,p-1);
    solve(l,p-1), solve(p+1,r);
}

signed main() {
    read(n,k);
    For(i,1,n) read(a[i]), s[i] = (s[i-1] + a[i]) %k;
    st::init();
    solve(1,n);
    For(i,0,n) {
        ++cnt[s[i]];
        for(int j = head[i]; j; j = nxt[j]) ans += op[j] * cnt[val[j]];
    }
    printf("%lld",ans-n);
    return 0;
}

上界 $O(nlog(n))$

或者传统的以 $mid$ ,严格 $O(nlog(n))$

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=300010,M=1100000;
int t[M][2],a[N],s[N],mx[N],pos[N],n,k,H;
long long ans;
inline void read(int &e)
{
    e=0;int w=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch)){e=e*10+ch-'0';ch=getchar();}
    e*=w;return;
}
inline void fenzhi(int l,int r)
{
    if(l==r)
        return;
    int mid=(l+r)>>1;
    s[mid]=H=mx[0]=0;
    for(int i=mid+1;i<=r;i++)
    {
        if(a[i]>a[mx[H]])
            mx[++H]=i;
        s[i]=((s[i-1]+a[i])%k+k)%k;
        t[((s[i]-a[mx[H]])%k+k)%k][0]++;
        pos[i]=mx[H];
    }
    mx[H+1]=r+1;
    int p = 1,z = 0,p1 = mid + 1,mx1 = 0;
    for(int i=mid;i>=l;i--)
    {
        z=(z+a[i])%k,mx1=max(mx1,a[i]);
        while(p<=H&&a[mx[p]]<=mx1)
            ++p;
        while(p1<mx[p])
        {
            t[(s[p1]-a[pos[p1]]%k+k)%k][0]--;
            t[s[p1]][1]++;
            p1++;
        }
        ans+=t[(k+mx1%k-z)%k][1];
        if(p<=H)ans+=t[(k-z)%k][0];
    }
    for(int i=mid+1;i<p1;++i)
        t[s[i]][1]--;
    for(int i=p1;i<=r;++i)
        t[(s[i]-a[pos[i]]%k+k)%k][0]--;
    fenzhi(l,mid); 
    fenzhi(mid + 1,r);
}
int main ()
{
    read(n);read(k);
    for(int i = 1;i <= n; ++i)
    {
        read(a[i]);
    }
    fenzhi(1,n);
    printf("%lld
",ans);
    return 0;
}

 T2 简单的玄学

显然有:

$egin{align}ans=frac{prod_{i=n-m+1}^{n-1}2^n-i}{2^{n(m-1)}}\end{align}$

考场:不难发现连乘部分是两端阶乘之商,而 $m$ 范围较大,阶乘中极易“经过”模数 $1000003$ 的倍数,那么只需要预处理至模数的阶乘就可以,对作为除数的阶乘求逆元。

但如果作为阶乘取模变为0,但是两者仍然要约分怎么办,可以先取模再约分吗?如果先约分再取模是不是太了,应该是先取模再约分,举了个例子没有发现问题。

于是炸了。

题解:先取模再约分的话就太了,所以先约分再取模。

显然有 $2^n-i$ 中2的个数等于 $i$ 中2的个数。

故对上面的式子分母两次快速幂 $egin{align}a^{b} mod s=a^{b mod (s-1)} mod send{align}$ ,之后统计分子中2的因数个数,相当于统计 $(m-1)!$ 阶乘中2的因数个数:核心代码:

long long ercnt=n;
    for(long long i=1;(1LL<<i)<m;i++)
    {
        ercnt+=(m-1)/(1LL<<i);
    }

约分后取模,可以取模后分子分母同乘2的逆元的因数个数幂。切记不要想当然除gcd。

#include<bits/stdc++.h>
using namespace std;
const int mod=1e6+3,phi=1e6+2;
long long jc[mod+100],a,b,n,m,ercnt;
long long quick_pow(long long x,long long y)
{
    long long rec=1;
    while(y)
    {
        if(y&1)
        {
            rec=rec*x%mod;
        }
        y>>=1;
        x=x*x%mod;
    }
    return rec;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    long long ercnt=n;
    for(long long i=1;(1LL<<i)<m;i++)
    {
        ercnt+=(m-1)/(1LL<<i);
    }
    long long maxn=quick_pow(2,n%phi);
    b=quick_pow(maxn,m%phi);
    a=1;
    for(long long i=0;i<m;i++)
    {
        a=a*(maxn-i)%mod;
        if(!a)
            break;
    }
    b=b*quick_pow(500002,ercnt)%mod;//2逆元500002
    a=a*quick_pow(500002,ercnt)%mod;
    printf("%lld %lld
",(b-a+mod)%mod,b);
    return 0;
}

T3 简单的填充

考场:没看

DP,维护 $up$ 和 $down$,分别表示当前格子最大可能能放的数字以及这个数字是第几次重复,和当前格子最小可能能放的数字以及这个数字是第几次重复。遍历时两者都继承上一个格子,上限每重复两次就更新,下限则是五次。如果当前格子有值就用它纠正二者,矛盾则impossible。为满足题意,最后剩下的格子(与 $n$ 点间没有确定的数)尽量多放大数。

#include<bits/stdc++.h>
using namespace std;
const int N=1100000;
int upc[N],upx[N],a[N],n,downc[N],downx[N],tong[N]; 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    if(a[1]!=1&&a[1]!=0)
    {
        puts("-1");
        return 0;
    }
    upc[1]=1;upx[1]=1;
    downc[1]=1;downx[1]=1;
    for(int i=2;i<=n;i++)
    {
        upc[i]=upc[i-1];upx[i]=upx[i-1];
        downc[i]=downc[i-1];downx[i]=downx[i-1];
        if(++upc[i]>2)
            upc[i]=1,upx[i]++;
        if(++downc[i]>5)
            downc[i]=1,downx[i]++;
        if(a[i])
        {
            if(upx[i]>a[i])
            {
                upx[i]=a[i];
                upc[i]=2;
            }
            else if(upx[i]==a[i])
            {
                upc[i]=min(upc[i],2);
            }
            if(downx[i]<a[i])
            {
                downx[i]=a[i];
                downc[i]=1;
            }
            if(upx[i]<a[i]||downx[i]>a[i])
            {
                puts("-1");
                return 0;
            }
        }
    }
    if(upc[n]==1)
    {
        upx[n]=upx[n-1];
    }
    if(upx[n]<downx[n])
    {
        puts("-1");
        return 0;
    }
    printf("%d
",upx[n]);
    tong[upx[n]]++;
    a[n]=upx[n];
    for(int i=n-1;i>=1;i--)
    {
        if(!a[i])
        {
            int tmp=min(a[i+1],upx[i]);
            if(tong[tmp]==5)
                tmp--;
            a[i]=tmp;
        }
        tong[a[i]]++;
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d ",a[i]);
    }
    return 0;
}

T1&T2都几乎正解思路但是打成不如暴力分QWQ

原文地址:https://www.cnblogs.com/HKHbest/p/15003338.html