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");
    }
}

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;
}

所有数不超过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;
}

满分做法:

二分出一个最大的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]<<‘\n‘;
    }
    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<<‘\n‘;
    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<<‘\n‘;
    }
    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<<‘\n‘;
    }
    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)<<‘\n‘;
    }
    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<<‘\n‘;
        }
        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]<<‘\n‘;
}

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<<‘\n‘;
            else cout<<query(1,1,n,l,r).mx[w]<<‘\n‘;
        }
        else change(1,1,n,l,r,w);
    }
}

错误:

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

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

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

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

时间: 2024-10-13 09:50:00

2017 清北济南考前刷题Day 3 morning的相关文章

2017 清北济南考前刷题Day 5 afternoon

期望得分:100+100+30=230 实际得分:0+0+0=30 T1 直接模拟 #include<cstdio> #include<iostream> using namespace std; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } #define N 1002

2017 清北济南考前刷题Day 1 morning

期望得分:100+100+50=250 实际得分:100+60+50=210 T2 二分 估错上界.估错复杂度 T1 立方数(cubic) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK定义了一个数叫“立方数”,若一个数可以被写作是一个正整数的3次方,则这个数就是立方数,例如1,8,27就是最小的3个立方数. 现在给定一个数P,LYK想要知道这个数是不是立方数. 当然你有可能随机输出一些莫名其妙的东西来骗分,因此LYK有T次询问~ 输入格式(cub

2017 清北济南考前刷题Day 5 morning

期望得分:100+100+0=200 实际得分: 坐标的每一位不是0就是1,所以答案就是 C(n,k) #include<cstdio> #include<iostream> using namespace std; const int mod=1e9+7; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0';

2017 清北济南考前刷题Day 2 morning

期望得分:100+30+60=190 实际得分:100+30+30=160 T1 最优方案跳的高度一定是单调的 所以先按高度排序 dp[i][j] 跳了i次跳到j 枚举从哪儿跳到j转移即可 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 51 struct node { int c,h; }e[N]

清北考前刷题day7早安

清北考前刷题da7下午好

三向城 /* 原图一定是一棵完全二叉树. 根节点是x,左节点是x*2,右节点是x*2+1 转化为二进制往左右走就很明显了. */ #include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; int T,x,y,k,ans,pos; inline int read() { int x=0,f=1;char c=getchar(); while

清北考前刷题day6下午好

/* 贪心 负数一定不取 枚举最高位是1 且答案取为0的 位置, 更新答案. */ #include<iostream> #include<cstdio> #include<cstring> #define ll long long #define N 100010 using namespace std; int n; ll a[N],ans,sum[N]; char s[N]; ll read() { ll x=0,f=1;char c=getchar(); whi

2017清北精英班整理内容掌握考试题

精英班考试题 2017.2.10 题目名 工资 藏妹子之处 银河之星 源文件 money.cpp/c/pas excel.pas/cpp galaxy.cpp/c/pas 输入文件 money.in excel.in galaxy.in 输出文件 money.out excel.out galaxy.out 时间限制 1000MS 1000MS 1000MS 内存限制 256MB 128MB 256MB 测试点 10 10 10 测试点分值 10 10 10 第一题 工资 链接 第二题  藏妹子

2017清北学堂集训笔记——图论

我们进入一个新的模块——图论! emmmmm这个专题更出来可能有点慢别介意,原因是要划的图和要给代码加的注释比较多,更重要的就是...这几个晚上我在追剧!!我们的少年时代超级超级超级好看,剧情很燃啊!!咳咳,好吧下面回归正题. 一.图的存储: 1.邻接矩阵: 假设有n个节点,建立一个n×n的矩阵,第i号节点能到达第j号节点就将[i][j]标记为1(有权值标记为权值), 样例如下图: 1 /*无向图,无权值*/ 2 int a[MAXN][MAXN];//邻接矩阵 3 int x,y;//两座城市