湖南NOIP集训模拟题DAY1 BY ExfJOE [贪心][DP][二分]

11 篇文章 0 订阅
8 篇文章 0 订阅

解题报告:
一. Maximum
题意:给定一串序列,根据输入的变量求其中的任意两个变量xor,and或or的最大值;
分析:
Std:FWT;
不会的话,and可以用每个位上有1的个数来贪心判断,具体的实现就是递归位数,把当前正在枚举的位数为1的数存在一个数组里,如果当前位数为1的数大于等于二就把筛选后数组传入下一步,否则把筛选前数组传入下一步;

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1<<20,maxn2=1e5;
int n,c,t,a[maxn2],b[maxn],exi[maxn*2+5],ans;
void buildy(int u,int ste,int cur)
{
    exi[cur]=1;
    if(ste==-1)return;
    if(a[u]&(1<<ste))buildy(u,ste-1,2*cur+1);
    else buildy(u,ste-1,2*cur);
}
int lookfor(int u,int ste,int cur)
{
    if(ste==-1)return 0;
    if(a[u]&(1<<ste)&&exi[2*cur])
    return lookfor(u,ste-1,2*cur)+(1<<ste);
    else if((!(a[u]&(1<<ste)))&&exi[2*cur+1])
    return lookfor(u,ste-1,2*cur+1)+(1<<ste);
    else if(exi[2*cur])
    return lookfor(u,ste-1,2*cur);
    return lookfor(u,ste-1,2*cur+1);
}
void work1(int *num,const int nn,int len)
{
    int s[nn],nxt=0;
    for(int i=len;i>=0;i--){
        for(int j=1;j<=nn;j++)if(num[j]&(1<<i))s[++nxt]=num[j];
        if(nxt>1){work1(s,nxt,i-1);
        ans+=1<<i;return;
        }
        memset(s,0,sizeof(s));nxt=0;
    }
    return ;
}
int work2()
{
    int mx=0;memset(exi,0,sizeof(exi));
    for(int i=1;i<=n;i++)
    buildy(i,19,1);
    for(int i=1;i<=n;i++)
    mx=max(lookfor(i,19,1),mx); 
    return mx;
}
int  work3()
{
    memset(b,0,sizeof(b));int mx=0;
    for(int i=1;i<=n;i++)b[a[i]]=1;
    for(int i=0;i<20;i++)for(int j=0;j<maxn;j++)if(j&(1<<i))
    b[j^(1<<i)]|=b[j];
    for(int i=1;i<=n;i++){
    int ans=0,tmp=0;
    for(int j=19;j>=0;j--)if((!(a[i]&(1<<j)))&&b[tmp|(1<<j)]){
    ans|=1<<j;tmp|=1<<j;
    }
    else if(a[i]&(1<<j))ans|=1<<j;
    mx=max(mx,ans);
    }
    return mx;
    /*for(int i=1;i<=n;i++)b[a[i]]=1;
    for(int i=0;i<=20;i++)
        for(int j=0;j<=maxn;j++)
            if(j&(1<<i))b[j^(1<<i)]|=b[j];
    int mx=0;
    for(int i=1;i<=n;i++)
    {
        int ans=a[i];
        int temp=0;
        for(int j=20;j>=0;j--)
            if(!(a[i]&(1<<j)))
                if(b[temp|(1<<j)])
                {
                    ans|=(1<<j);
                    temp|=(1<<j);
                }
        mx=max(mx,ans);
    }
    return mx;*/
}
int main()
{
    freopen("maximum.in","r",stdin);
    freopen("maximum.out","w",stdout);
    scanf("%d",&t);
    for(int i=1;i<=t;i++){
    scanf("%d %d",&n,&c);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    if(c==1)ans=0,work1(a,n,19),printf("%d\n",ans);
    else if(c==2)printf("%d\n",work2());
    else printf("%d\n",work3());
    }
    return 0;
}

二. Bridge
题意:给定坐标轴上的点的坐标和移动方向,求某时间之后某个点的位置,两个点在遇到的瞬间改变方向,每组数据包括多次查询;
分析:首先,每个点遇到的时候立即改变方向,即点的相对位置不会改变。
然后,两个点相遇以后可以看做两个点交换编号并且穿过。
所以对于当前时间,对于当前状态可以O(1)求出任意点编号转换后的位置;
所以这道题包括两次二分思想,一次是针对当前时间二分位置,然后可以求出当前位置前面的点和后面的点有多少个。就可以判断当前点在第几。
然后对不同方向的点分别排序,二分查找有多少点在当前位置的前面(因为速度确定,那么对于每一种方向,需要在某个点之前才能在当前点之前的数值也唯一确定);
然后要注意的就是,当满足情况的时候要多次右移或左移,取决于参考的是当前点之前还是之后的点数,这样才能确定当前枚举的位置就是那个点刚好的位置。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=2e5 +5;
int pos[maxn],dir[maxn],rk[maxn],lef[maxn],rig[maxn];
int n,rigcnt,lefcnt,q,num,tii;
int main()
{
    freopen("bridge.in","r",stdin);
    freopen("bridge.out","w",stdout);
    scanf("%d",&n);
    for(int i=0;i<n;i++)scanf("%d",&pos[i]);
    for(int i=0;i<n;i++)scanf("%d",&dir[i]);
    for(int i=0;i<n;i++){
        rk[i]=pos[i];
        if(dir[i])rig[rigcnt++]=pos[i];
        else lef[lefcnt++]=pos[i];
    }
    sort(rk,rk+n);sort(rig,rig+rigcnt);sort(lef,lef+lefcnt);
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        scanf("%d %d",&num,&tii);
        long long  l=rk[0]-tii,r=rk[n-1]+tii;
        int  x=lower_bound(rk, rk + n, pos[num]) - rk +1;   
        while(l<=r){
        long long mid=(l+r)/2;
        int tmp1=(upper_bound(rig,rig+rigcnt,mid-tii)-rig )
                  +(upper_bound(lef,lef+lefcnt,mid+tii)-lef );
            if(tmp1<x)l=mid+1;
            else  r=mid-1;
        }
        printf("%d\n",(int)l);
    }
    return 0;
}

三. Group
题意:对给定数据分组,使得每一组数据的极差和不超过K,求分组的方式;
分析:用F[I][J][k]表示处理到i点,还有J组没有确定最大值,一共极差和已经是k的情况一共有多少种,那么最终的答案就是F[N][0][K],K<=kmaxn的和,显然我们知道,当处理到i时,除了i-1的数据,剩下的数据都没有用,所以这里可以用滚动数组cur和nxt来存储数据,节约空间。
对于每个点有四种情况,一种是新建一组不加,一种是新建一组等待最大值,一种是加入一组等待最大值,一种是加入一组不加。注意,要处理加入一组的最大值问题,本题输入顺序需要排序。
然后就是,对于代价,用差分的方式,每次加上两个点的差乘以j,因为只有没确定最大值的点才需要继续加,每确定一个点,它修改的状态的j就减一,保证了该状态的该组不再被加。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int mody=1e9+7;
const int maxn=205,maxn2=1005;
int n,a[maxn],f[2][maxn][maxn2],kava,cur,nxt,ans;
int main()
{
    freopen("group.in","r",stdin);
    freopen("group.out","w",stdout);
    scanf("%d %d",&n,&kava);
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    sort(a,a+n);
    cur=0;nxt=1;f[cur][1][0]=f[cur][0][0]=1;
    for(int i=1;i<n;i++){
    for(int j=0;j<=i+1;j++)for(int k=0;k<=kava;k++){
        int v=f[cur][j][k];f[cur][j][k]=0;
        if(!v)continue;
        int t=k+(a[i]-a[i-1])*j;
        if(t>kava)continue;
        (f[nxt][j][t]+=v)%=mody;
        (f[nxt][j+1][t]+=v)%=mody;
        (f[nxt][j][t]+=(long long)v*j%mody)%=mody;
        if(j)(f[nxt][j-1][t]+=(long long)v*j%mody)%=mody;

    }
    swap(cur,nxt);
    }
    for(int i=0;i<=kava;i++)(ans+=f[cur][0][i])%=mody;
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值