zoukankan      html  css  js  c++  java
  • 2017 清北济南考前刷题Day 3 morning

    实际得分:100+0+0=100

    T1

    右上角是必败态,然后推下去

    发现同行全是必胜态或全是必败态,不同行必胜必败交叉

    列同行

    所以n,m 只要有一个是偶数,先手必胜

    #include<cstdio>
    
    using namespace std;
    
    int main()
    {
        freopen("star.in","r",stdin);
        freopen("star.out","w",stdout);
        int n,m;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            if(!n) return 0;
            if(!(n&1) || !(m&1)) puts("Yuri");
            else puts("Chito");
        }
    }
    View Code

    T2

    k=1 暴力:

    可持久化trie树 求 异或最大值

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    #define N 50001
    
    const int mod=1e9+7;
    
    using namespace std;
    
    int bit[31];
    
    int a[N];
    
    int root[N],ch[N*31][2],cnt[N*31],tot;
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    void insert(int pre,int &now ,int dep,int x)
    {
        if(!now) now=++tot;
        cnt[now]=cnt[pre]+1;
        if(dep<0) return;
        int p= (x&bit[dep])>0;
        ch[now][p^1]=ch[pre][p^1];
        insert(ch[pre][p],ch[now][p],dep-1,x);
    }
    
    int query(int pre,int k,int dep,int x)
    {
        if(dep<0) return 0;
        int p= (x&bit[dep])>0;
        if(cnt[ch[k][p^1]]-cnt[ch[pre][p^1]]) return bit[dep]+query(ch[pre][p^1],ch[k][p^1],dep-1,x);
        return query(ch[pre][p],ch[k][p],dep-1,x);
    }
    
    int main()
    {
        freopen("war.in","r",stdin);
        freopen("war.out","w",stdout);
        bit[0]=1;
        for(int i=1;i<=30;++i) bit[i]=bit[i-1]<<1;
        int n,k;
        read(n); read(k);
        for(int i=1;i<=n;++i) 
        {
            read(a[i]);
            insert(root[i-1],root[i],31,a[i]);
        }
        int ans=0;
        for(int i=1;i<=n;++i)  ans=max(ans,query(root[0],root[n],31,a[i]));
        cout<<ans%mod;
    }
    View Code

    所有数不超过1023暴力:

    预处理所有 i^j的结果,cnt[i]表示第i个数的个数

    这样每一种 异或 值出现的次数=cnt[i]*cnt[j]

    从大到小枚举,直至k个即可

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    #define N 50001
    
    const int mod=1e9+7;
    
    using namespace std;
    
    int cnt[1026];
    
    struct node
    {
        int i,j,y;
    }e[1024*1024+5];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    bool cmp(node p,node q)
    {
        return p.y>q.y;
    }
    
    int main()
    {
        freopen("war.in","r",stdin);
        freopen("war.out","w",stdout);
        int n,x; long long k;
        read(n); scanf("%I64d",&k);
        for(int i=1;i<=n;++i) read(x),cnt[x]++;
        int tot=0;
        for(int i=0;i<1024;++i)
            for(int j=i+1;j<1024;++j)
                e[++tot].i=i,e[tot].j=j,e[tot].y=i^j;
        sort(e+1,e+tot+1,cmp);
        int ans=0;
        for(int i=1;i<=tot;++i)
        {
            ans=(ans+min(k,1ll*cnt[e[i].i]*cnt[e[i].j])*e[i].y%mod)%mod;
            k-=min(k,1ll*cnt[e[i].i]*cnt[e[i].j]);
            if(!k) break;
        }
        cout<<ans;
    }
    View Code

    满分做法:

    二分出一个最大的t,t满>t的数至少有k个

    然后查询所有异或值 >t 的数的和,最后在减去 属于前k大的数的和

    二分检验与查询均在trie树上进行

     

    查询>t的数的个数:

    枚举n个数a[i],累积 与每一个a[i] 异或后 >t 的个数  

    对于每一个a[i],设现在是第j位

    如果t的第j位是0,那么累加第j位与a[i] 不同 的数的个数,trie树上的当前位置转到 第j位与a[i] 的第j位相同的位置

    因为所以在第j位就分出大小的数都以累加,继续找第j位分不出大小的数

    如果t的第j位是1,什么都不累加,trie树上的当前位置 转到 第j位与a[i] 不同的 位置

    因为如果这一位与t的第j位相同,异或得0,一定<t,如果不同,第j位 分不出大小,继续往后走

     

    累积所有>t的数的和:

    还是枚举n个数a[i],累积 与每一个 a[i] 异或后>t 的数的和

    累积方法思路与上面差不多

    如果t的第j位是0,那么就累计 当前位置 所有的与a[i] 异或 为1的数的和,当前位置转到 与a[i]的第j位相同的位置

    如果t的第j为是1,那么当前位置 转到与a[i]的第j位不同的位置

    注意每一对合法的数会使用两次,所以累计个数 和 总和 时 注意 除以2

    #include<cstdio>
    #include<iostream>
    
    using namespace std;
    
    #define N 50001
    
    const int mod=1e9+7;
    const int inv=5e8+4;
    
    long long k; int n;
    
    int a[N],trie[N*31][2],all[N*31][31],tot=1;
    
    int cnt[N*31];
    
    int bit[31];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    } 
    
    void insert(int x)
    {
        int now=1,d;
        for(int i=30;i>=0;--i)
        {
            d=(x&bit[i])>0;
            if(!trie[now][d]) trie[now][d]=++tot;
            now=trie[now][d];
            cnt[now]++;
            for(int j=30;j>=0;--j) 
                if(x&bit[j]) all[now][j]++;
        }
    }
    
    int upper(int x,int t)
    {
        int pos=1,d,sum=0;
        for(int i=30;i>=0;--i)
        {
            d=(x&bit[i])>0;
            if(!(t&bit[i])) sum+=cnt[trie[pos][d^1]],pos=trie[pos][d];
            else pos=trie[pos][d^1];
        //    if(t==754974719 && x==962029906) cout<<i<<' '<<sum<<' '<<trie[pos][d^1]<<'
    ';
        }
        return sum;
    }
    
    int query(int x,int t)
    {
        int pos=1,d,sum=0;
        for(int i=30;i>=0;--i)
        {
            d=(x&bit[i])>0;
            if(!(t&bit[i])) 
            {
                for(int j=30;j>=0;--j) 
                if(x&bit[j]) sum=(sum+1LL*(cnt[trie[pos][d^1]]-all[trie[pos][d^1]][j])*bit[j]%mod)%mod;
                else sum=(sum+1LL*all[trie[pos][d^1]][j]*bit[j]%mod)%mod;
                pos=trie[pos][d];
            }
            else pos=trie[pos][d^1];
        }
        //cout<<sum<<'
    ';
        return sum;
        
    }
    
    long long check(int x)
    {
        long long sum=0;
        for(int i=1;i<=n;i++) 
        {
            sum+=upper(a[i],x);
            //if(x==754974719) cout<<i<<' '<<sum<<'
    ';
        }
        return sum/2;
    }
    
    int main()
    {
        freopen("war.in","r",stdin);
        freopen("war.out","w",stdout);
        read(n); scanf("%I64d",&k);
        bit[0]=1;
        for(int i=1;i<=30;++i) bit[i]=bit[i-1]<<1;
        for(int i=1;i<=n;++i)
        {
            read(a[i]);
            insert(a[i]);
        //    cout<<i<<' '<<tot<<'
    ';
        }
        int l=0,r=2147483647,mid,tmp=-1;
        while(l<=r)
        {
            mid=l+r>>1;
            if(check(mid)>=k) l=mid+1,tmp=mid;
            else r=mid-1;
          //  cout<<mid<<' '<<check(mid)<<'
    ';
        }
        int ans=0;
        if(tmp!=-1)
        {
            long long res=k-check(tmp);
            for(int i=1;i<=n;i++) 
            {
                ans+=query(a[i],tmp),ans%=mod;
            //    cout<<ans<<'
    ';
            }
            ans=1LL*ans*inv%mod;
            ans=(ans+1LL*res*(tmp+1)%mod)%mod;
        }
        else
        {
            for(int i=1;i<=n;i++) ans+=query(a[i],0),ans%=mod;
            ans=1LL*ans*inv%mod;
        }
        cout<<ans;
        //for(int i=1;i<=tot;i++) cout<<i<<' '<<cnt[i]<<'
    ';
    }
    View Code

     T3

    因为k<=10 所以线段树维护区间前10大

    合并的时候,用了类似于归并排序时用的 两个指针,扫一遍即可

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace std;
    
    #define N 100001
    
    int f[N<<2];
    
    struct node
    {
        int mx[11];
        
        node() { memset(mx,0,sizeof(mx));}
        
        node operator + (node p) 
        {
            node t;
            int a=1,b=1,c=1;
            while(c<=10) t.mx[c++]=mx[a]>p.mx[b] ? mx[a++] : p.mx[b++];
            return t;
        }
    }tr[N<<2];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    void build(int k,int l,int r)
    {
        if(l==r) { read(tr[k].mx[1]); return; }
        int mid=l+r>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        tr[k]=tr[k<<1]+tr[k<<1|1];
    }
    
    void down(int k)
    {
        f[k<<1]+=f[k];
        f[k<<1|1]+=f[k];
        for(int i=1;i<=10;++i)
        {
            if(tr[k<<1].mx[i]) tr[k<<1].mx[i]+=f[k];
            if(tr[k<<1|1].mx[i]) tr[k<<1|1].mx[i]+=f[k];
        } 
        f[k]=0;
    }
    
    node query(int k,int l,int r,int opl,int opr)
    {
        if(l>=opl && r<=opr) return tr[k];
        if(f[k]) down(k);
        int mid=l+r>>1;
        if(opr<=mid) return query(k<<1,l,mid,opl,opr);
        if(opl>mid) return query(k<<1|1,mid+1,r,opl,opr);
        return query(k<<1,l,mid,opl,opr)+query(k<<1|1,mid+1,r,opl,opr);
    }
    
    void change(int k,int l,int r,int opl,int opr,int w)
    {
        if(l>=opl && r<=opr)
        {
            for(int i=1;i<=10;i++) 
                if(tr[k].mx[i]) tr[k].mx[i]+=w;
            f[k]+=w;
            return;
        }
        if(f[k]) down(k);
        int mid=l+r>>1;
        if(opl<=mid) change(k<<1,l,mid,opl,opr,w);
        if(opr>mid) change(k<<1|1,mid+1,r,opl,opr,w);
        tr[k]=tr[k<<1]+tr[k<<1|1];
    }
    
    int main()
    {
        freopen("noname.in","r",stdin);
        freopen("noname.out","w",stdout);
        int n,m;
        read(n); read(m);
        build(1,1,n);
        int op,l,r,w;
        while(m--)
        {
            read(op); read(l); read(r); read(w); 
            if(!op) 
            {
                if(r-l+1<w) cout<<-1<<'
    ';
                else cout<<query(1,1,n,l,r).mx[w]<<'
    ';
            }
            else change(1,1,n,l,r,w);
        }
    }
    View Code

     错误:

    将前10大全部放到一个结构体里,query时直接返回结构体

    合并的时候 重载的 加号 运算符

    所以 标记 不能放到 结构体里

    下传标记的时候,只传前10大,但应先判断是否具有第i大

  • 相关阅读:
    UVA 558 Wormholes
    HDU 1565 方格取数(1)
    poj2607
    poj2552
    poj2491
    poj2502
    poj2613
    .NET Framework 4 与 .NET Framework 4 Client Profile的区别与联系
    .Net Framework 4.0 和 2.0/3.0/3.5
    企业IT系统
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7755041.html
Copyright © 2011-2022 走看看