普及模拟1

普及模拟1

T1 Past 0pts

  • 简化题意:已知一个长度为 n(1n3×106) 的序列 a ,给定一个 d(0d3) ,有如下操作(所有操作对 1336363663 取模):

    • 1d 时,求 l=1nr=lnk=lrak
    • 2d 时,求 l=1nr=ln(maxk=lr{ak}mink=lr{ak})
    • 3d 时,求 n!×l=1nlen=1nl+1k=ll+len1aklen
  • 部分分:

    • O(n3) 暴力枚举:TLE 50pts
    • O(n2+nlog2(n))
      • 线段树维护极差:TLE 50pts

      • ST 表维护极差:MLE+TLE 40pts

        • ST 表数组开太大了,全部 MLE 挂了 40pts

  • 正解:

    • d=1
      • 打表发现第 i 个数对答案产生的贡献为 ai×i×(ni+1) ,最后的答案即为 i=1nai×i×(ni+1)
        • 证明:包含第 i 个数的子序列的左端点一定在 [1,i] 内,右端点一定在 [i,n] 内。依据乘法原理,包含第 i 个数的子区间一共有 i×(ni+1) 个,故第 i 个数对答案产生的贡献为 ai×i×(ni+1)
    • d=2
      • 考虑将式子拆开,有 l=1nr=lnmaxk=lr{ak}mink=lr{ak}=l=1nr=lnmaxk=lr{ak}l=1nr=lnmink=lr{ak} 。令 fi=l=1imaxk=li{ak},gi=l=1imink=li{ak} ,即 fi 表示以 i 结尾的所有子序列的最大值之和,gi 表示以 i 结尾的所有子序列的最小值之和,最终答案即为 i=1n(figi) 。在 1i 中,找到一个 p 使得 ap>ai ,且 p 最大(若不存在,则有 p=0 ),那么就得到了状态转移方程 fi=fp+ai(ip) ,将 p 用单调栈进行维护,对于 g 同理。
    • d=3
      • 预处理出阶乘数组 mul1,mul2 和前缀和数组 sum ,使得对于每一个 i(1in) 均满足 mul1,i=k=1iak,mul2,i=k=inak,sumi=k=1nak ,接着将式子拆开, n!×l=1nlen=1nl+1k=ll+len1aklen=l=1nlen=1nl+1(n!len×k=ll+len1ak) ,这样就省去了对浮点数和除法的计算(便于取模)。打表,得到每个元素对这段子序列的答案贡献次数如下:
        • 观察发现,长度为 len 的子序列对答案产生的贡献为长度为 len1的子序列对答案产生的贡献+sumnlen+1sumlen1 ,那么维护一个临时变量 ls 存储当前对答案产生的贡献,然后乘上 mul1,i1,mul1,i+1 即可。即 lslen=lslen1+sumnlen+1sumlen1 ,最终答案即为 len=1nlslen×mul1,len1×mul2,len+1 ,边界为 ls0=0
  • 本题卡负数取模,记得有减法运算时先加模数再取模,加法、乘法运算时先取模再运算再取模。

  • 本程序取模较多,吸氧(开 O2 )才能过,时间复杂度为 O(n)

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long 
    #define sort stable_sort 
    #define endl '\n'
    const ll p=1336363663;
    ll a[3000001],f[3000001],g[3000001],mul1[3000001],mul2[3000001],sum[3000001];
    stack<ll>maxx,minn;
    int main()
    {
        freopen("pst.in","r",stdin);
        freopen("pst.out","w",stdout);
        ll n,d,i,j,ans1=0,ans2=0,ans3=0,ls=0;
        scanf("%lld%lld",&n,&d);
        mul1[0]=mul2[n+1]=1;
        for(i=1,j=n;i<=n,j>=1;i++,j--)
        {
            scanf("%lld",&a[i]);
            sum[i]=(sum[i-1]%p+a[i]%p)%p;
            mul1[i]=((mul1[i-1]%p)*(i%p))%p;
            mul2[j]=((mul2[j+1]%p)*(j%p))%p;
        }
        if(d>=1)
        {
            for(i=1;i<=n;i++)
            {
                ans1=(ans1+(((((a[i]%p)*(i%p))%p)%p)*((n-i+1)%p)%p))%p;
                while(maxx.empty()==0&&a[maxx.top()]<=a[i])
                {
                    maxx.pop();
                }
                while(minn.empty()==0&&a[minn.top()]>=a[i])
                {
                    minn.pop();
                }
                if(maxx.empty()==0)
                {
                    f[i]=(f[maxx.top()]%p+(a[i]%p)*((i-maxx.top())%p))%p;
                }
                else
                {
                    f[i]=(f[0]%p+(a[i]%p)*(i%p)%p)%p;
                }
                if(minn.empty()==0)
                {
                    g[i]=(g[minn.top()]%p+(a[i]%p)*((i-minn.top())%p))%p;
                }
                else
                {
                    g[i]=(g[0]%p+(a[i]%p)*(i%p)%p)%p;
                }
                maxx.push(i);
                minn.push(i);
                ls=(ls+sum[n-i+1]%p-sum[i-1]%p+p)%p;
                ans3=(ans3+((ls*(mul1[i-1]%p))%p*(mul2[i+1]%p))%p)%p;
            }
            for(i=1;i<=n;i++)
            {
                ans2=(ans2+(f[i]-g[i]+p)%p)%p;
            }
            printf("%lld\n",ans1);
            if(d>=2)
            {
                printf("%lld\n",ans2);
                if(d>=3)
                {
                    printf("%lld\n",ans3);
                }
            }
        }
        return 0;
    }  
    

T2 Present 100pts

  • 简化题意:对一个序列,进行两端插入,两端读取,两端删除,反转的操作。

  • luogu B3656 【模板】双端队列 1 变形,用数组模拟或 deque 维护数列,考虑开一个 bool 类型的 flag 记录是否进行颠倒。

    • 每次颠倒时,将 flag 取反即可。
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long 
    #define sort stable_sort 
    #define endl '\n'
    deque<ll>q;
    int main()
    {
        freopen("prs.in","r",stdin);
        freopen("prs.out","w",stdout);
        ll n,id,pd,x,i;
        bool flag=false;
        scanf("%lld%lld",&n,&id);
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&pd);
            if(pd==0)
            {
                scanf("%lld",&x);
                if(flag==false)
                {
                    q.push_front(x);
                }
                else
                {
                    q.push_back(x);
                }
            }
            if(pd==1)
            {
                if(q.empty()==0)
                {
                    if(flag==false)
                    {
                        printf("%lld\n",q.front());
                        q.pop_front();
                    }
                    else
                    {
                        printf("%lld\n",q.back());
                        q.pop_back();
                    }
                }
                else
                {
                    printf("0\n");
                }
            }
            if(pd==2)
            {
                scanf("%lld",&x);
                if(flag==false)
                {
                    q.push_back(x);
                }
                else
                {
                    q.push_front(x);
                }
            }
            if(pd==3)
            {
                if(q.empty()==0)
                {
                    if(flag==false)
                    {
                        printf("%lld\n",q.back());
                        q.pop_back();
                    }
                    else
                    {
                        printf("%lld\n",q.front());
                        q.pop_front();
                    }
                }
                else
                {
                    printf("0\n");
                }
            }
            if(pd==4)
            {
                flag=(!flag);
            }
        }
        return 0;
    }  
    
  • 貌似是首A。

T3 Future 100pts

  • 简化题意:给定一棵 n 个节点的树,问从根节点(编号为 1 的节点)到叶子结点的最大距离。

  • 类似 luogu P1807 最长路 ,但本题是一棵树,跑树形 DPSPFA拓扑优化 DP 求最长路 等做法皆可。

    • 树形 DP 的一种做法,令 fai(1in) 表示 i 的父节点,fi(1in) 表示以 i 为终点时,所得到的收获值,则有状态转移方程 fi=ffai+wi
  • 坑:点权有负数,输出结果应初始化为

    • 最后 40min 检查时才发现,差点挂了 10pts

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long 
    #define sort stable_sort 
    #define endl '\n'
    struct node
    {
        ll nxt,to;
    }e[400001];
    ll w[400001],f[400001],head[400001],dout[400001],cnt=0;//注意与上文数组定义不同
    void add(ll u,ll v)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    void dfs(ll x,ll fa)
    {
        f[x]=f[fa]+w[x];
        for(ll i=head[x];i!=0;i=e[i].nxt)
        {
            if(e[i].to!=fa)
            {
                dfs(e[i].to,x);
            }
        }
    }
    int main()
    {
        freopen("ftr.in","r",stdin);
        freopen("ftr.out","w",stdout);
        ll n,i,u,ans=-0x7f7f7f7f;
        scanf("%lld",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&w[i]);
        }
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&u);
            add(u,i);
            dout[u]++;//统计出度,为了找到终点
        }
        dfs(1,0);
        for(i=1;i<=n;i++)
        {
            if(dout[i]==0)
            {
                ans=max(ans,f[i]);
            }
        }
        printf("%lld\n",ans);
        return 0;
    }  
    

T4 Beyond 0pts

  • 简化题意:给定一个 n×n(2n20) 大小的矩阵和常数 fate,记 past 为从 (1,1) 到对角线(即直线 y=nx+1 上)的路径上每个方格的异或和, future 为从对角线走到 (n,n) 的路径上每个方格的异或和。求 past+fate=future的方案数。
  • 强化版:CF1006F Xor-Paths | [ABC271F] XOR on Grid Path
  • 部分分:
    • 10pts :输出 0
  • 正解:考虑爆搜(明显的暗示,其实是 meet in middle ),分别从 (1,1),(n,n) 进行爆搜,当到达对角线时,将所得到的值分别用两个 map 进行存储,又因为在对角线可以进行传送至另一个对角线,然后利用加法原理和乘法原理求解。
    • 对于每个 pastfuture 中用 map.find() 或其他方式寻找 past+fate 出现的次数,即可进行求解。
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long 
    #define sort stable_sort 
    #define endl '\n'
    ll c[21][21],f[21][21],dil[2][2]={{1,0},{0,1}},dir[2][2]={{-1,0},{0,-1}};
    map<ll,ll>l,r;
    map<ll,ll>::iterator it,val;
    void dfs1(ll x,ll y,ll n,ll num)
    {
        if(y==n-x+1)
        {
            l[num]++;
        }
        else
        {
            ll i,nx,ny;
            for(ll i=0;i<=1;i++)
            {
                nx=x+dil[i][0];
                ny=y+dil[i][1];
                if(1<=nx&&nx<=n&&1<=ny&&ny<=n)
                {
                    dfs1(nx,ny,n,num^c[nx][ny]);
                }
                
            }
        }
    }
    void dfs2(ll x,ll y,ll n,ll num)
    {
        if(y==n-x+1)
        {
            r[num]++;
        }
        else
        {
            ll i,nx,ny;
            for(ll i=0;i<=1;i++)
            {
                nx=x+dir[i][0];
                ny=y+dir[i][1];
                if(1<=nx&&nx<=n&&1<=ny&&ny<=n)
                {
                    dfs2(nx,ny,n,num^c[nx][ny]);
                }
            }
        }
    }
    int main()
    {
        freopen("byd.in","r",stdin);
        freopen("byd.out","w",stdout);
        ll n,fate,i,j,ans=0,sum;
        scanf("%lld%lld",&n,&fate);
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                scanf("%lld",&c[i][j]);
            }
        }
        dfs1(1,1,n,0);
        dfs2(n,n,n,0);
        for(it=l.begin();it!=l.end();it++)
        {
            val=r.find(it->first+fate);
            if(val!=r.end())
            {
                ans+=(it->second)*(val->second);//将两个数出现的次数相乘
            }
        }
        printf("%lld\n",ans);
        return 0;
    }  
    

总结

  • 十年 OI 一场空,不开 long long 见祖宗。

  • 本次开题顺序: (T2,T3,T1,T4) ,在这里没有掉进出题人的坑里。

  • T4 没有再认真想想,如果想到爆搜的话可能分数再高点(?)。

  • 注意文件输入输出(有人在这里爆零了)。

后记

posted @   hzoi_Shadow  阅读(144)  评论(11编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
扩大
缩小
点击右上角即可分享
微信分享提示