zoukankan      html  css  js  c++  java
  • NOIP 鸡王争霸赛

    NOIP 鸡王争霸赛

    竞赛时间: 2018 年 8 月 23 日
    By –老王
    最终测试均打开 O2 优化



    bag vest helmet
    bag.in vest.in helmet.in
    bag,out vest.out helmet.out
    2s 2s 2s
    512MB 512MB 512MB
    忽略行末空格和
    回车
    忽略行末空格和
    回车
    忽略行末空格和
    回车
    传统 传统 传统




    Problem A:三级包(bag.c/cpp/pas)

    Input file: bag.in
    Output file: bag.out
    Time limit: 2 seconds
    Memory limit: 512 megabytes


    老王被同学拉去吃鸡,一落地就捡到了三级包,同时地上还有很多其他老王
    想要带走的东西, M416、 5.56 子弹、垂直握把、 8 倍镜等等,但是显然老王不
    能带走所有的东西。老王的三级包比较奇特,不仅有重量限制,而且有物品个数
    限制。地上的每一种装备个数都记为 1 并有自己的重量,老王想要在不超过限制
    的情况下带走尽可能重的东西,为了帮助老王吃鸡,请你来完成这个问题。
    Input
    第一行两个正数 n、 m 分别表示三级包的个数限制和容量限制
    第二行一个整数 k 表示地上的装备数量
    接下来一行 k 个整数, 第 i 个整数

    Output
    输出一共一行一个整数, 表示老王能带走的最大重量

    Example

    Sample Input1

    5 100
    8
    8 64 17 23 91 32 17 12

    Sample Output1

    99

    Sample Input2

    5 10
    3
    99 99 99

    Sample Output2

    0

    Notes
    30%的数据,满足k ≤ 15
    另有20%的数据,满足k ≤ 20
    另有20%的数据,满足n ≤ 6
    对于100%的数据, k ≤ 40n ≤ 35, 未提及的数据均是110^9之间的整数。

    30 分解法: 2 ^n爆搜即可
    50 分解法: 30 分做法随便剪枝优化一下
    70 分解法: 因为n ≤ 6,可以从1~n枚举选取物品个数,然后
    爆搜, C[k][6]也没多大,所以跑过毫无压力(+50 分做法)

    100 分解法:
    先预处理前一半物品, 枚举这部分物品的所有选取集合, 并
    用 set 存下来 S[i]里面存放选取物品数量不超过 i 的所有权值
    集合。同样,枚举后一半物品的所有选取集合,并到对应的
    set 里面二分查找满足要求的最大权值即可,时间复杂度
    O(tlogt)

    感谢黄学长(神犇)原代码

    //
    #include<stdio.h>
    #include<bits/stdc++.h>
    using namespace std; 
    typedef long long ll;
    int w[50],n,m,k;
    int cnt1[1<<21],cnt2[1<<21];
    ll sum1[1<<21],sum2[1<<21],ans;
    vector<ll> v[50];
    int lowbit(int x)
    {
        return x&-x;
    }
    int main()
    {
         cin>>n>>m>>k; 
        for(int i=1;i<=k;i++)cin>>w[i];
        if(k==1)
        {
            if(w[1]<=m)cout<<w[1];
            else cout<<0;
            return 0;
        }
        int mid=k/2;
        int tot=(1<<mid)-1;
        for(int i=1;i<=mid;i++)sum1[1<<i-1]=w[i];//压缩1集合 
        for(int s=1;s<=tot;s++)
        {
            sum1[s]=sum1[s^lowbit(s)]+sum1[lowbit(s)];//s集合为sum1[s^lowbit(s)]的集合(s去掉lowbits位)情况加上lowbit位的情况 (lowbits位为一) 
            cnt1[s]=cnt1[s^lowbit(s)]+1;//个数限制 
            if(sum1[s]<=m&&cnt1[s]<=n)//满足条件 
            {
                ans=max(ans,sum1[s]);
                v[cnt1[s]].push_back(sum1[s]);//在选择cnt1个物品的情况下的一个代价 
            }
        }
        for(int i=1;i<=mid;i++)sort(v[i].begin(),v[i].end());
        for(int i=1;i+mid<=k;i++)sum2[1<<i-1]=w[i+mid];//压缩2集合 
        mid=k-mid;
        tot=(1<<mid)-1;
        vector<ll>::iterator it;
        for(int s=1;s<=tot;s++)
        {
            sum2[s]=sum2[s^lowbit(s)]+sum2[lowbit(s)];
            cnt2[s]=cnt2[s^lowbit(s)]+1;
            if(cnt2[s]<=n&&sum2[s]<=m)
            {
                ans=max(ans,sum2[s]);
                for(int j=1;j<=n-cnt2[s];j++)//和一集合匹配 
                if(!v[j].empty())
                {
                    it=upper_bound(v[j].begin(),v[j].end(),m-sum2[s]);//二分查找满足要求的最大权值 
                    if(it!=v[j].begin())
                    {
                        it--;
                        ans=max(ans,(*it)+sum2[s]);
                    }
                }
            }
        }
        cout<<ans;
        return 0;
    }

    Problem B:三级甲(vest.c/cpp/pas)

    Input file: vest.in
    Output file: vest.out
    Time limit: 2 seconds
    Memory limit: 512 megabytes

    在皮卡多捡到了一件三级甲的老王非常兴奋, 想马上测试一下三级甲的性能,
    于是老王走到了加油站旁边的空地上跳俄舞,不一会儿身上就被打了一排子弹,
    老王躲起来后脱下三级甲,发现这一排弹孔深度不一,聪明的老王想到了一个量
    化计算性能的方法,给定一个 k(k ≤ n),计算出所有长度为 k 的区间的弹孔深度
    的最小值的和,当然 k 值不同,得到的性能值也不同,所以老王会取不同的 k 进
    行计算,为了帮助老王吃鸡,这个问题就交给你来解决。
    Input
    第一行两个正数 n、 m 分别表示弹孔个数和询问次数
    第二行共 n 个整数,表示这一排每个弹孔的深度

    Output
    输出共 m 行,每行输出一个整数表示对应询问的答案

    Example

    Sample Input1

    2 2
    1 1
    1

    2

    Sample Output1

    2

    1

    Sample Input2

    5 3
    10 3 20 30 11

    2

    4

    5

    Sample Output2

    37

    6

    3

    Sample Input3

    9 5
    11 33 78 55 26 100 200 89 98
    2

    3

    4

    5

    6

    Sample Output3

    429

    300

    204

    115

    89

    30 分解法:随便怎么做
    50 分解法:分析每一个元素对于不同询问区间长度的贡献,
    l 为它到左边第一个比他小元素的距离, r 为它到右边第一个
    比他小元素的距离,那么可以发现,贡献呈这种趋势“
    于是可以预处理每个元素的 l, r, O(1)即可得到每个元素对
    所询问区间长度的贡献,于是O(  n^2)

    60 分解法: 50 分+送的 10 分
    100 分解法: 注意到,所询问就是如上图中多个梯形,在某
    一个 x 处的权值和,而上述梯形又显然可分成三段,两段公
    差为 1 的等差数列和一段常数数列,于是三段分开前缀后缀
    之类的O(n)预处理一下,每个询问O(1),于是最终复杂度
    O(n)

    //
    #include<stdio.h>
    #include<bits/stdc++.h>
    using namespace std;
    inline char nc()
    {
        static char buf[100000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int rd()
    {
        char ch=nc();
        int sum=0;
        while(!(ch>='0'&&ch<='9'))ch=nc();
        while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
        return sum;
    }
    typedef long long ll;
    ll ans1[1000005],ans2[1000005];
    int n,m;
    int a[1000005],l[1000005],r[1000005];
    stack<int> s;
    int main()
    {
        n=rd(),m=rd(); 
        for(int i=1; i<=n; i++)
        {
            a[i]=rd();
        }
        s.push(0);
        for(int i=1; i<=n; i++)
        {
            while(!s.empty()&&a[s.top()]>a[i])//如果a[i]小于栈顶元素
            {
                r[s.top()]=i-1; //栈顶最右不能超过a[i]
                s.pop();
            }
            if(!s.empty()&&a[s.top()]==a[i])//特判相等
            {
                l[i]=l[s.top()];//左取小于等于 
                r[s.top()]=i-1;//右不取
                s.pop();
            }
            else l[i]=s.top()+1;//栈顶元素小于a[i],a[i]最左不能超过顶元素
            s.push(i);//i号入栈
        }
        while(!s.empty())r[s.top()]=n,s.pop();// 栈内还有元素则说明没有a[i]小于等于栈顶元素,则栈顶元素最小,最右影响到n号
        int L,R;
        for(int i=1; i<=n; i++)//讨论i号点的贡献
        {
            L=i-l[i]+1;//L到R区间分情况讨论
            R=r[i]-i+1;
            if(L<=R)
                //if(k<=L)ans+=k*a[i];梯形左边
                //if(L<k<R)ans+=L*a[i];梯形上边
                //if(R<=k<=R+L)ans+=(R+L-k)*a[i];梯形右边
                //分有无k进行差分
            {
                if(L>1)
                {
                    ans2[1]+=a[i];//差分ans2:a[i] 
                    ans2[L]-=a[i];
                }
                ans1[L]+=1ll*L*a[i];
                ans1[R+1]-=1ll*L*a[i];
                if(R+1<=L+R-1)
                {
                    ans1[R+1]+=1ll*(R+L)*a[i];
                    ans1[L+R]-=1ll*(R+L)*a[i];
                    ans2[R+1]-=a[i];
                    ans2[L+R]+=a[i];
                }
            }
            else
            {
                if(R>1)
                {
                    ans2[1]+=a[i];
                    ans2[R]-=a[i];
                }
                ans1[R]+=1ll*R*a[i];
                ans1[L+1]-=1ll*R*a[i];
                if(L+1<=L+R-1)
                {
                    ans1[L+1]+=1ll*(L+R)*a[i];
                    ans1[L+R]-=1ll*(L+R)*a[i];
                    ans2[L+1]-=a[i];
                    ans2[L+R]+=a[i];
                }
            }
        }
        int x;
        for(int i=1; i<=n; i++)ans1[i]+=ans1[i-1],ans2[i]+=ans2[i-1];
        for(int i=1; i<=n; i++)ans1[i]+=ans2[i]*i;//补回 k*a[i]
        while(m--)
        {
            x=rd();
            printf("%lld
    ",ans1[x]);
        }
    }

    Problem C:三级头(helmet.c/cpp/pas)

    Input file: helmet.in
    Output file: helmet.out
    Time limit: 2 seconds
    Memory limit: 512 megabytes


    捡到三级甲不久后,老王发现了一个空投,走进发现周围全是尸体,而且每
    个尸体上都有不同破烂程度的三级头,而且所有尸体又恰好构成了一棵树形结构,
    老王正奇怪,突然从箱子背后跳出一个吉利服男子问老王问题,如果打对了就把
    装备送给老王:最开始以 1 号尸体为根,有两种操作,

    (1)询问以 i 为根的子树中

    三级头耐久度严格大于 k 的有几个;

    (2)指定 i 为根;

    (3)修改 i 号尸体的三级头耐

    久度为 x。为了帮助老王吃鸡,请你来回答这个问题!
    Input
    第一行两个正整数 n、 m 分别表示尸体个数和操作次数;
    第二行 n 个整数 wj分别表示每个尸体上三级头的耐久度;
    接下来 n-1 行每行两个整数 i、 j,表示 i、 j 之间有边相连;
    一个空行之后, 接下来 m 行,每行表示一次操作:
    若是第一种操作,则有三个整数: 1、 i、 k;
    若是第二种操作, 则有两个整数: 2、 i;
    若是第三种操作,则有三个整数: 3、 i、 x。

    Output
    输出行数与 1 类型操作次数相等,每行表示对应操作的答案

    Example

    Sample Input1

    5 3
    15 18 20 8 10
    4 3
    4 2
    3 1
    2 5
    1 4 8
    3 2 1
    1 3 10

    Sample Output1

    2

    1

    Sample Input2

    7 4
    26 22 8 5 21 28 5
    6 1
    6 3
    1 7
    1 4
    4 2
    7 5
    1 3 22
    1 1 10
    2 7
    1 4 5

    Sample Output2

    0

    4

    1

    Notes
    20%的数据, n, m ≤ 2000
    另有40%的数据, 没有 3 类型操作
    对于100%的数据, n, m ≤ 1000001 ≤ wj≤10^9

    20 分做法:随你怎么做
    60 分做法: 没有三类型操作,这就意味着权值是恒定的,离
    线即可。处理出每一个询问时相应的根,并将询问按照询问
    的权值排序, dfs 序+树状数组解决即可(如果新根在子树外,
    dfs 序区间无影响,若在子树内,则分为前后两个区间)
    100 分做法: dfs 序后分块, 并维护块内有序, 这样询问时,
    到对应的块中二分查找一下即可,多出来的暴力处理即可,
    时间复杂度O(nt logt)

     1 #include<stdio.h>
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 struct node
     5 {
     6     int to;
     7     int next;
     8 }edge[300000];
     9 int head[100005],num;
    10 long long a[100005],dep[100005],fa[100005],A[100005];
    11 int n,q,aa,bb,xx,yy;
    12 void add(int x,int y)
    13 {
    14     edge[++num].to=y;
    15     edge[num].next=head[x];
    16     head[x]=num;
    17 }
    18 void dfs(int u,int Fa)
    19 {
    20     fa[u]=Fa;
    21     dep[u]=dep[Fa]+1;
    22     for(int i=head[u];i;i=edge[i].next)
    23     {
    24         int v=edge[i].to;
    25         if(v!=Fa)
    26         dfs(v,u);
    27     }
    28 }
    29 bool OK()
    30 {
    31 int cnt=0;
    32     while(xx!=yy)
    33     {
    34         if(dep[xx]<dep[yy])swap(xx,yy);
    35         A[++cnt]=a[xx];
    36         xx=fa[xx];
    37         if(cnt>36)return 1;
    38     }
    39     A[++cnt]=a[xx];
    40     if(cnt<4)return 0;
    41     sort(A+1,A+cnt+1);
    42    for(int i=4;i<=cnt;i++)
    43    if(A[i]<A[i-1]+A[i-2]+A[i-3])return 1;
    44     return 0;
    45 }
    46     
    47 int main()
    48 {
    49     ios::sync_with_stdio(false);
    50     cout.tie(NULL);
    51     cin>>n;
    52     for(int i=1;i<=n;i++)
    53     cin>>a[i];
    54     for(int i=1;i<n;i++)
    55     {
    56         cin>>aa>>bb;
    57         add(aa,bb);
    58         add(bb,aa);
    59     }
    60     dfs(1,0);
    61     cin>>q;
    62     for(int i=1;i<=q;i++)
    63     {
    64         cin>>aa;
    65         if(aa==1)
    66         {
    67             cin>>xx>>yy;
    68             a[xx]=yy;
    69         }
    70         if(aa==2)
    71         {
    72             cin>>xx>>yy;
    73             if(OK())cout<<"Yes"<<endl;
    74             else cout<<"No"<<endl;
    75         }
    76     }
    77 }
    78     
    79     
  • 相关阅读:
    Query Designer:变量的偏移 Variable Offset
    Query Designer:变量注意事项
    Query Designer:Variable 变量
    Query Designer:Hierarchy层级显示
    Query Designer:公式冲突
    Analyzer中进行货币转换
    Analyzer报表里显示的 * 星号、红叉、#井号的意义
    增量测试
    BW系统之间的InfoProvider数据传输:Export DataSource
    DSO的记录模式Record Mode字段测试
  • 原文地址:https://www.cnblogs.com/CXYscxy/p/11758046.html
Copyright © 2011-2022 走看看