NOIP模拟题 2016.10.5 [Trie] [数学] [二分答案] [杂题] [复杂状态DP]

T1:
题意:求序列中两两取& | ^的最大值。。

这道题相当于三道题。。

XOR就是Trie树上贪心即可,注意一开始不可以加入0这个串,并且插入a[1]后从i=2开始匹配。
另外,匹配的时候两种写法。
第一种取反后匹配,这里要注意取反后不能限制最高位!!因为有可能超出最高位的位置可以填1!!
第二种的话,就是一边匹配一边用ans记录,或者说还是用单词节点,只不过匹配的时候要每位取反。。

AND操作首先应让最高位为1,那么只有一个数在这个二进制位上是1的话,那么这一位就应该填入0,并且去掉那个1 ,如果有两个以上,那么这位一定为1,再删掉所有该位不为1的数,注意这里同时要删掉低位为1的个数。

OR操作,对于一个数a[i],其二进制的所有子集所对应的数都可以用a[i]来填补(因为是|),那么首先把所有可以用a数组中某个数填补的数用数组标记,然后枚举每一个a[i],以及第二个数的每一位,判断加上这一位之后能不能被填补,最后得到的就是最大值。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<list>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 100005;
const int maxbit = 23;
int a[maxn];
int n;
struct Trie
{
    int son[maxn<<5][2];
    int key[maxn<<5];
    int maxnode;
#define son(x,d) son[x][d]
#define key(x) key[x]
    void clear() { memset(son,0,sizeof(son)); memset(key,0,sizeof(key)); maxnode=0; }
    void insert(int val)
    {
        int root=0;
        for(int k=21;k>=1;k--)
        {
            int d = (val&(1<<k-1))>>k-1;
            if(!son(root,d)) son(root,d)=++maxnode;
            root = son(root,d);
        }
        key(root) = val;
    }
    int query(int x)
    {
        /*int tmp = x , highest = 0;
        while(tmp) highest++,tmp>>=1;
        int to = (~x)&((1<<highest)-1);*/
        int to = (~x);
        int root = 0;
        for(int k=21;k>=1;k--)
        {
            int d = (to&(1<<k-1))>>k-1;
            if(son(root,d)) root = son(root,d);
            else root = son(root,d^1);
        }
        return key(root) ^ x;
    }
    /*int query(int x)
    {
        int root = 0;
        for(int k=21;k>=1;k--)
        {
            int d = (x&(1<<k-1))>>k-1; d^=1;
            if(son(root,d)) root = son(root,d);
            else root = son(root,d^1);
        }
        return key(root) ^ x;
    }*/
}trie;
int _xor()
{
    trie.clear();
    //trie.insert(0); // musn't insert the first one !!! coz 0 is possibly considered!!
    int ans = 0; trie.insert(a[1]);
    for(int i=2;i<=n;i++) // start from the 2nd !!! coz the existence needs to be provided!!
    {
        smax(ans , trie.query(a[i]));
        trie.insert(a[i]);
    }
    return ans;
}
int cnt[maxbit];
bool disable[maxn];
int _and()
{
    int ans = 0;
    memset(cnt,0,sizeof(cnt));
    memset(disable,0,sizeof(disable));
    for(int i=1;i<=n;i++)
        for(int p=1;p<=20;p++)
            if(a[i]&(1<<p-1)) cnt[p]++;
    for(int p=20;p>=1;p--) if(cnt[p]>=2)
    {
        ans |= (1<<p-1);
        for(int i=1;i<=n;i++) if(!disable[i])
            if(!(a[i]&(1<<p-1)))
            {
                disable[i]=true;
                for(int pp=1;pp<=20;pp++)
                    if(a[i]&(1<<pp-1)) cnt[pp]--;
            }
    }
    return ans;
}
bool g[1<<maxbit];
int _or()
{
    memset(g,0,sizeof(g));
    int highest=0,tmp=*max_element(a+1,a+n+1);
    while(tmp) highest++,tmp>>=1;
    int lim = 1<<highest;
    for(int i=1;i<=n;i++) g[a[i]]++;
    for(int p=1;p<=20;p++)
        for(int s=0;s<lim;s++)
            if(s&(1<<p-1)) g[s-(1<<p-1)]|=g[s];
    int ans = 0;
    for(int i=1;i<=n;i++)
    {
        int now = a[i] , to = 0;
        for(int p=20;p>=1;p--) if(!(now&(1<<p-1)))
        {
            to |= (1<<p-1);
            if(!g[to]) to -= (1<<p-1);
        }
        smax(ans,now|to);
    }
    return ans;
}
int main()
{
    freopen("maximum.in","r",stdin);
    freopen("maximum.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int op;
        scanf("%d%d",&n,&op);
        for(int i=1;i<=n;i++) scanf("%d",a+i);
        switch(op)
        {
            case 1: printf("%d",_and()); break;
            case 2: printf("%d",_xor()); break;
            case 3: printf("%d",_or()); break;
        }
        if(T) putchar('\n');
    }
    return 0;
}

T2:
题意:在数轴上有n个点,每个点有一个初始方向,速度都是1,然后询问第i个点在t时刻的位置

首先明确,对于任意一个点,最终的排名是不变的,并且相撞时相当于两个点交换了位置。
那么把所有点分成向左和向右两种,每次查询第i个点原来的顺序后,现在要找一个位置,使得向左走的点和向右走的点个数之和是原来的排名。
二分答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
const int INF=0x3f3f3f3f;
const int maxn = 200005;
inline void read(int &x)
{
    x = 0;
    int flag = 1;
    char ch = getchar();
    while(ch<'0' || ch>'9')
    {
        ch=getchar();
        if(ch == '-') flag = -1;
    }
    while(ch>='0' && ch<='9')
    {
        x*=10; x+=ch-'0';
        ch = getchar();
    }
    x *= flag;
}
int n,q;
LL lt[maxn],rt[maxn],mt[maxn];
int pos[maxn];
int mxl,mxr,mxm;
int main()
{
    freopen("bridge.in","r",stdin);
    freopen("bridge.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) read(pos[i]),mt[++mxm]=pos[i];
    for(int i=1;i<=n;i++)
    {
        int d;
        read(d);
        if(d) rt[++mxr] = pos[i];
        else lt[++mxl] = pos[i];
    }
    sort(lt+1,lt+mxl+1);
    sort(rt+1,rt+mxr+1);
    sort(mt+1,mt+mxm+1);
    read(q);
    while(q--)
    {
        int x,y;
        read(x); read(y); x++;
        int standard = lower_bound(mt+1,mt+mxm+1,pos[x])-mt;
        LL l = (LL)mt[1] - y , r = (LL)mt[n] + y;
        while(l<r)
        {
            LL m = (l+r)>>1;
            int tmp = (upper_bound(lt+1,lt+mxl+1,m+y)-lt-1) + (upper_bound(rt+1,rt+mxr+1,m-y)-rt-1);
            if(tmp < standard) l=m+1;
            else r=m;
        }
        printf("%d",(int)l);
        if(q) putchar('\n');
    }
    return 0;
}

T3:
题意:有n个同学,每个同学有一个值,求分成任意组数每组的极差之和不超过k的方案数。

很迷的一道DP。。。
a数组排序后,用f[i][j][k]表示处理到第i个数,有j个还可以填数的组,当前的极差之和为k的方案数。
有四种转移方式:

  • 新建一个组,不加入任何数
  • 新建一个组,加入一个数,并且该集合只有这一个数
  • 加入一个已有的组,不作为最大值
  • 加入一个已有的组,并且作为最大值

用差分加速。
把 Sj - Si 差分成很多段,每一次加入之后,当前还有j个未封顶的集合,每一个集合都加上(Ai - Ai-1),总的代价 (Ai - Ai-1) * j

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
const int INF=0x3f3f3f3f;
const int mod = 1000000007;
const int maxn = 205;
const int maxk = 1005;
int n,m;
int s[maxn];
int f[2][maxn][maxk];
int main()
{
    freopen("group.in","r",stdin);
    freopen("group.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",s+i);
    sort(s+1,s+n+1);
    int now=0,last=1;
    f[now][1][0]=f[now][0][0]=1; // the first number into given to a new but not settled or a new settled set!!
    for(int i=2;i<=n;i++)
    {
        swap(now,last);
        memset(f[now],0,sizeof(f[now]));
        for(int j=0;j<=n;j++)
            for(int k=0;k<=m;k++)
            {
                int base = f[last][j][k];
                if(!base) continue;
                int to = (s[i]-s[i-1])*j + k; // delta composition
                if(to>m) break;
                (f[now][j][to]+=base)%=mod; // a new set with only one element
                (f[now][j+1][to]+=base)%=mod; // a new set empty and unlimited
                (f[now][j][to]+=(LL)base*j%mod)%=mod; // into an existed set but now the maximum
                if(j) (f[now][j-1][to]+=(LL)base*j%mod)%=mod; // into an existed set and to be the maximum element
            }
    }
    int ans = 0;
    for(int k=0;k<=m;k++) (ans+=f[now][0][k])%=mod;
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值