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     
原文地址:https://www.cnblogs.com/CXYscxy/p/11758046.html