zoukankan      html  css  js  c++  java
  • 11月2日考试 题解(前缀和+哈希+树状数组+树链剖分)

    T1 计算异或和

    题目大意:给定一个长度为$n$的序列$a_i$,设$b_i=a_i oplus imod 1 oplus imod 2oplus cdots oplus imod n$,求出$q_1oplus q_2oplus cdots oplus q_n$。

    可以单独把$i mod k$这样一类式子提出来,发现有循环节,前缀异或和维护一下即可。

    代码:

    #include<cstdio>
    #include<iostream>
    #define int long long
    using namespace std;
    const int N=1000005;
    int p[N],sum[N],n,ans;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    signed main()
    {
        n=read();
        for (int i=1;i<=n;i++) 
        {
            p[i]=read();
            sum[i]=i^sum[i-1];ans^=p[i];
        }    
        for (int i=2;i<=n;i++)
        {
            int num=n/i,rest=n%i;
            if (num&1) ans^=sum[i-1];
            ans^=sum[rest];
        }
        printf("%lld",ans);
        return 0;
    }

    T2 配置香水

    给定长度为$n$的序列$a_i$和整数$k$,问有多少$[l,r]$满足$sumlimits_{i=l}^r a_i=k^j(jgeq 0)$。

    发现题目要求形如这样$sum_r-sum_l=k^i$,我们变换一下形式:$sum_r-k_i=sum_l$。于是可以枚举$i$,然后用哈希表维护一下看有多少个合法的$sum_l$即可。

    用了map成功被卡掉50分常数

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define int long long
    using namespace std;
    const int N=100005,M=500005;
    const int up=1e14;
    const int mod=499999;
    int T,n,k,sum[N],ans;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    struct hash{
        int nxt[500005],hd[500005],cnt,a[500005],b[500005];
        void clear(){
            memset(hd,0,sizeof(hd));
            cnt=0;
        }
        void insert(int x){
            int t=((x%mod)+mod)%mod;
            nxt[++cnt]=hd[t];
            a[cnt]=x;
            b[cnt]=1;
            hd[t]=cnt;
        }
        bool find(int x){
            int t=((x%mod)+mod)%mod;
            for(int i=hd[t];i;i=nxt[i]){
                if(a[i]==x) return 1;
            }
            return 0;
        }
        void add(int x){
            int t=((x%mod)+mod)%mod;
            for(int i=hd[t];i;i=nxt[i]){
                if(a[i]==x) b[i]++;
            }
        }
        int ask(int x){
            int t=((x%mod)+mod)%mod;
            for(int i=hd[t];i;i=nxt[i]){
                if(a[i]==x) return b[i];
            }
            return 0;
        }
    }h;
    inline void solve(int x)
    {
        h.clear();
        for (int i=0;i<=n;i++)
        {
            if (h.find(sum[i])) h.add(sum[i]);
            else h.insert(sum[i]);
            ans+=h.ask(sum[i]-x);
        }
    }
    signed main()
    {
        T=read();
        while(T--)
        {
            n=read();k=read();ans=0;
            for (int i=1;i<=n;i++)
                sum[i]=sum[i-1]+read();
             if(k==1){
                solve(1);
                printf("%lld
    ",ans);
                continue;
            }
            if(k==-1){
                solve(1);solve(-1);
                printf("%lld
    ",ans);
                continue;
            }
            int kk=1;
            while(kk<=(long long)1000000000*n){
                solve(kk);
                kk*=k;        
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }

    T3 奥法之劫

    题目大意:给定长度为$n$的序列$a_i,p_i$和长度为$m$的序列$b_i$。$p_i$为删掉$a_i$的代价,$b_i$单调递增。现要求删掉一些数,使得能从中依次选出$m$个数组成$b_i$,且对于任意$iin[1,m]$,满足$b_{i-1}$和$b_i$之间所有数都小于$b_{i-1}$。求最小代价。

    考场上写出来了$n^2$DP,想到了$nlog n$做法然而没调出来,自闭了。

    设$f_{i,j}$表示$a$考虑到$i$,$b$考虑到$j$时的最小代价。显然对于$a_i$和$b_j$的大小关系有三种情况,分别转移就好。然后发现$j$这一维可以省去,因为对于$a_i<b_j$和$a_i>b_j$的情况它们都由$f_{i-1,j}$转移过来且后面加的都是个常数,且$j$显然是一段区间,所以可以数据结构维护。对于$a_i=b_j$的情况可以单点修改,时间复杂度$O(nlog n)$。然而它写挂了QAQ。

    提供另一种$nlog n$的做法,好写好调。大致思路是维护一个权值树状数组,每次对于$a_i=b_j$的情况进行转移。每次找出大于$b_{k-1}$小于$b_k$的$a_i$,然后计算它们对答案的贡献。

    正解复杂度是$O(n)$的,然而我并不太会。

    代码:

    #include<cstdio>
    #include<iostream>
    #define lowbit x&-x
    #define ll long long
    using namespace std;
    const int N=5000005;
    const ll inf=1e18;
    ll tree[N],f[N];
    int cnt[N],a[N],p[N],b[N],pos[N],n,m;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void add(int x,int k){while(x){tree[x]+=k;x-=lowbit;}}
    inline ll query(int x){ll sum=0;while(x<=n+1){sum+=tree[x];x+=lowbit;}return sum;}
    inline void solve()
    {
        cnt[0]=1;
        for (int i=1;i<=n;i++) f[i]=inf;
        for (int i=1;i<=n;i++)
        {
            int k=pos[a[i]]; ll ff=0;
            if (k&&cnt[k-1]>0) ff=query(b[k-1]+1)+f[k-1];
            add(p[i]>=0?a[i]:n+1,p[i]);
            if (k&&cnt[k-1]>0)
            {
                ff-=query(b[k]+1);
                f[k]=min(f[k],ff);
                ++cnt[k];
            }
        }
        if (!cnt[m]){
            puts("Impossible");
            return;
        }
        ll ans=f[m]+query(b[m]+1);
        printf("%lld",ans);
    }
    signed main()
    {
        n=read();
        for (int i=1;i<=n;i++) a[i]=read();
        for (int i=1;i<=n;i++) p[i]=read();
        m=read();
        for (int i=1;i<=m;i++) b[i]=read(),pos[b[i]]=i;
        solve();
        return 0;
    }

    T4 多彩树

    题目大意:给定一棵含有$n$个节点的树,每个节点有颜色$c_i$。每次只能走向$(c_i+1)mod C$的节点。$q$次操作,带修,询问从$x$出发的极大联通块的大小。

    题解在有了。

  • 相关阅读:
    PostgreSQL 模式(SCHEMA)
    PostgreSQL学习---模式schema
    psql 工具详细使用介绍
    CentOS 下 VNC Server 的配置与使用
    如何处理/boot/efi/EFI/cento from install of fwupdate-efi
    uGUI练习(二) Animate UI
    uGUI练习(一) Anchor
    uGUI练习 开篇
    Fix "Missing Scripts"
    用uGUI开发自定义Toggle Slider控件
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13922012.html
Copyright © 2011-2022 走看看