NOIP复习模拟赛day3

1. 胖哥购物

(shopping.cpp/c/pas)

【问题描述】今天,胖哥在淘宝上购物。刚开始,胖哥的手上没有任何的优惠券。已知在购物过程中,胖哥将会按顺序遇到 n 个事件:1 x:购买一个价值为 x 的商品。当发生这种事时,胖哥最多只能使用一张面值小于 x的优惠券去减免。若手中没有优惠券,则全额购买。2 x:得到一张 x 元的优惠券。胖哥想知道,在最优的策略下,完成今天的购物最少要花掉多少钱。

【输入】共 n+1 行,第一行包含一个整数 n,表示事件的个数。接下来 n 行,每行两个正整数 type x。若 type=1,表示是事件 1,若 type=2,表示是事件 2。

【输出】输出共一行,包含一个整数,表示完成今天的购物最少要花掉多少钱。

【输入输出样例 1】10

         2 4

         1 6

         1 6

         2 4

         1 1

         2 2

         2 3

         2 3

         1 3

         1 6

 

         12

【数据说明】对于 30%的数据,1≤n≤10。对于 60%的数据,1≤n≤1000。对于 100%的数据,1≤n≤100000。对于 100%的数据,1≤x≤10000

官方题解:

【考察算法】贪心

对于百分30的数据,暴力即可。

对于每次购物,如果手上有优惠券,不用白不用,每次贪心选取可减免金额最高的优惠券来使用,即可通过百分60的数据。

考虑百分100的数据,n=10w,每次线性去查找减免金额最高的优惠券的话,复杂度太高。

如何高效率地找出减免金额最高的优惠券?

假设现在手中有m张优惠券,我们要购买的物品价值为x,那么我们需要在这m个数中,找出小于x的最大的数。这里可以用权值线段树、平衡树等数据结构去维护,或者直接使用stl的map,时间复杂度O(nlogn)。

自己bb的题解:

  我们考虑用一个set(万能的STL)来存储每次收集到的优惠券;

  如果不会的请点我

  然后就是一个lower_bound来找第一个大于等于当前价值优惠券;

  找到完了以后把优惠券扔了就好了;

 

  代码......

  

 1 //NOIPRP++
 2 #include<bits/stdc++.h>
 3 #define Re register int
 4 using namespace std;
 5 int N,Type,x;
 6 long long ans;
 7 multiset <int> s;
 8 inline void read(int &x){
 9     x=0; char c=getchar(); bool p=1;
10     for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0;
11     for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
12     p?:x=-x;
13 }
14 int main(){
15     Re i,j;
16     read(N);
17     while (N--){
18         read(Type); read(x);
19         if (Type==1){
20             if (s.empty()){ans+=x; continue;}
21             multiset <int>::iterator it=s.lower_bound(x);
22             if (it==s.begin()){ans+=x; continue;}
23             ans+=x-*(--it); s.erase(it);
24         }else s.insert(x);
25     }
26     printf("%lld",ans);
27     return 0;
28 }
29 //NOIPRP++
View Code

  其实,我考试用的不是这种方法;

  我很平静的开了一个大根堆和一个小根堆;

  然后手打堆;

  嗯,居然奇迹般的过了......

 1 //NOIPRP++
 2 #include<bits/stdc++.h>
 3 #define Re register int
 4 using namespace std;
 5 int N,Type,x,Num,Big[100005],Small[100005],Top,top;
 6 long long ans;
 7 inline void read(int &x){
 8     x=0; char c=getchar(); bool p=1;
 9     for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0;
10     for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
11     p?:x=-x;
12 }
13 inline void Push_Big(int x){
14     Big[++Top]=x;
15     for (Re i=Top;i^1&&Big[i>>1]<Big[i];i>>=1){Big[i]^=Big[i>>1];Big[i>>1]^=Big[i];Big[i]^=Big[i>>1];}
16 }
17 inline void Push_Small(int x){
18     Small[++top]=x;
19     for (Re i=top;i^1&&Small[i>>1]>Small[i];i>>=1){Small[i]^=Small[i>>1];Small[i>>1]^=Small[i];Small[i]^=Small[i>>1];}
20 }
21 inline void Pop_Big() {
22     Big[1]=Big[Top--];
23     for(Re p=1,q=2; q<=Top; p=q,q<<=1) {
24         if(q+1<=Top && Big[q]<Big[q+1]) ++q;
25         if(Big[p]<Big[q]){Big[p]^=Big[q];Big[q]^=Big[p];Big[p]^=Big[q];}
26         else break;
27     }
28 }
29 inline void Pop_Small() {
30     Small[1]=Small[top--];
31     for(Re p=1,q=2; q<=top; p=q,q<<=1) {
32         if(q+1<=top && Small[q]>Small[q+1]) ++q;
33         if(Small[p]>Small[q]){Small[p]^=Small[q];Small[q]^=Small[p];Small[p]^=Small[q];}
34         else break;
35     }
36 }
37 int main(){
38     Re i,j;
39     read(N);
40     for (i=1;i<=N;i++){
41         read(Type);read(x);
42         if (Type==1){
43             while (Top&&Big[1]>=x) Push_Small(Big[1]),Pop_Big();
44             while (top&&Small[1]<x) Push_Big(Small[1]),Pop_Small();
45             if (Top) ans+=x-Big[1],Pop_Big();
46             else ans+=x;
47         }
48         else if (Type==2) Push_Big(x);
49     }
50     printf("%lld",ans);
51     return 0;
52 }//NOIPRP++
View Code

 

如果不手打堆......(也行,但是要看脸......)

 1 //NOIPRP++
 2 #pragma GCC optimize("O2")
 3 #include<bits/stdc++.h>
 4 #define Re register int
 5 using namespace std;
 6 int N,Type,X;
 7 long long ans;
 8 priority_queue<int>q;
 9 priority_queue <int, vector<int> ,greater<int> > p;
10 inline void read(int &x){
11     x=0;char c=getchar();bool p=1;
12     for (;'0'>c||'9'<c;c=getchar()) if (c=='-') p=0;
13     for (;'0'<=c&&'9'>=c;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
14     p?:x=-x;
15 }
16 int main(){
17     Re i,j;
18     read(N);
19     for (i=1;i<=N;i++){
20         read(Type); read(X);
21         if (Type==1){
22             while (!q.empty()&&q.top()>=X) p.push(q.top()),q.pop();
23             while (!p.empty()&&p.top()<X) q.push(p.top()),p.pop();
24             if (!q.empty()) ans+=X-q.top(),q.pop();
25             else ans+=X; 
26         }
27         else q.push(X);
28     }
29     printf("%lld",ans);
30     return 0;
31 }
32 //NOIPRP++
View Code

2. 胖哥的树

(tree.cpp/c/pas)

【问题描述】胖哥有一棵 n 个节点的树,其中有一些节点是黑色的,另外一些节点是白色的。考虑这棵树上的一个 k 条边的边集。如果胖哥将这 k 条边从树上删除,则将会把这棵树分成 K+1 个联通块。现在,胖哥想知道,存在多少种这样的边集,使得删除这些边后,每个联通块只有一个黑色的节点。由于答案可能很大,请将答案 mod 1000000007(109+7)。

【输入】共三行,第一行包含一个整数 n,表示树的节点的个数。第二行有 n-1 个整数 P0,P1,...,Pn-2。pi 表示第(i+1)个节点与第 Pi 个节点之间有条边。假设树的节点的编号是从 0 到 n-1。第三个有 n 个整数 X0,X1,...,Xn-1(Xi=0 或 1)。如果 Xi等于 1,表示节点 i 是黑色,反之则表示节点 i 是白色。

【输出】输出共一行,包含一个整数,表示答案 mod 1000000007。

【输入输出样例 1】3

           0 0

           0 1 1

 

           2

 

【输入输出样例 2】6

           0 1 1 0 4

           1 1 0 0 1 0

 

           1

 

【输入输出样例 3】10

         0 1 2 1 4 4 4 0 8

         0 0 0 1 0 1 1 0 0 1

 

         27

 

【数据说明】对于 30%的数据,2≤n≤10。对于 60%的数据,2≤n≤1000。对于 100%的数据,2≤n≤10000

 

官方题解:

【考察算法】树型DP

对于百分30的数据,枚举每条边选或者不选,然后统计即可。

对于大点的数据,考虑树型DP。

设dp[i][0]代表i这个联通块没有黑点的方案数,dp[i][1]代表有一个黑点的方案数。

假设父亲节点为u,考虑儿子节点的情况:

l 如果儿子节点是个有黑点的,父亲节点也有黑点,那么只能分裂开,不能合并在一起,只对dp[u][1]有贡献

如果儿子节点是个有黑点的,父亲节点没有黑点,那么可以分裂也可以合并,对dp[u][1]和dp[u][0]都有贡献

如果儿子节点是个没有黑点的,父亲节点有黑点,那么必须和父亲合并,对dp[u][1]有贡献

l  如果儿子节点是个没有黑点的,父亲节点也没黑点,那么必须和父亲合并,对dp[u][0]有贡献

边界状态:

如果第i个节点为黑色,那么dp[i][0]=0,dp[i][1]=1;

如果第i个节点为白色,那么dp[i][0]=1,dp[i][1]=0;

注意中间过程可能会超出int范围,要开long long

 

自己bb的题解:

  其实官方题解已经讲得很清楚了;

  那就看一下转移方程吧...

   if root is black 
  f[root][0]=0
  f[root][1]=PI(f[son][0]+f[son][1])
  else //root is white
  f[root][0]=PI(f[son][0]+f[son][1])
  f[root][1]=sigma(f[son][1]*PI(son'!=son)(f[son'][0]+f[son'][1]))
  when inserting node v
  f[root][1]*=f[v][0]+f[v][1]
  f[root][1]+=f[v][1]*product
  update product
  还有像官方题解说的,记得开long long......
 
  代码......
 
  
 1 //NOIPRP++
 2 #include<bits/stdc++.h>
 3 #define Re register int
 4 #define MOD 1000000007
 5 #define LL long long 
 6 using namespace std;
 7 int N,f[100005][2],u,v,Num,head[100005],Color[100005];
 8 struct Edge{
 9     int To,Next;
10 }edge[200005];
11 inline void read(int &x){
12     x=0; char c=getchar(); bool p=1;
13     for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0;
14     for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
15     p?:x=-x;
16 }
17 inline void AddNum(int u,int v){
18     edge[++Num].Next=head[u];
19     edge[Num].To=v;
20     head[u]=Num;
21 }
22 inline void dfs(int Now,int Fa){
23     f[Now][Color[Now]]=1; f[Now][Color[Now]^1]=0;
24     for (Re i=head[Now];i;i=edge[i].Next){
25         int k=edge[i].To;
26         if (k==Fa) continue; dfs(k,Now); 
27         f[Now][1]=(1LL*f[Now][1]*(f[k][0]+f[k][1])%MOD+1LL*f[Now][0]*f[k][1]%MOD)%MOD;
28         f[Now][0]=1LL*f[Now][0]*(f[k][0]+f[k][1])%MOD;
29     }
30 }
31 int main(){
32     Re i,j;
33     read(N);
34     for (i=1;i<N;i++) read(v),AddNum(i,v),AddNum(v,i);
35     for (i=0;i<N;i++) read(Color[i]);
36     dfs(0,-1); printf("%d\n",f[0][1]);
37     return 0;
38 }
39 //NOIPRP++
View Code

 

 第三题嘛......

我只会暴力,但是居然拿了55分,震惊......

先看一下我暴力的代码吧,简单易懂......

 1 //NOIPRP++
 2 #include<bits/stdc++.h>
 3 #define Re register int
 4 using namespace std;
 5 int N,M,Q,Num,head[100005],No[100005],u,v,x,y,Sum;
 6 bool t[100005];
 7 char C[1];
 8 struct info{
 9     int x,y;
10 }f[100005];
11 struct Edge{
12     int To,Next;
13 }edge[400005];
14 inline void read(int &x){
15     x=0; char c=getchar(); bool p=1;
16     for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0;
17     for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
18     p?:x=-x;
19 }
20 inline void AddNum(int u,int v){
21     edge[++Num].Next=head[u];
22     edge[Num].To=v;
23     head[u]=Num;
24 }
25 inline bool cmp(info a,info b){return a.x<b.x;}
26 inline void dfs(int Now){
27     //cout<<Now<<endl;
28     f[++Sum].x=No[Now]; f[Sum].y=Now; t[Now]=false;
29     for (Re i=head[Now];i;i=edge[i].Next) if (t[edge[i].To]) dfs(edge[i].To);
30 }
31 int main(){
32     Re i,j;
33     read(N);read(M);
34     for (i=1;i<=N;i++) read(x),No[i]=x;
35     for (i=1;i<=M;i++){
36         read(u);read(v);
37         AddNum(u,v);AddNum(v,u);
38     }
39     read(Q);
40     while (Q--){
41         scanf("%s",C);read(x);read(y);
42         if (C[0]=='B'){
43             AddNum(x,y); AddNum(y,x);
44         }
45         else if (C[0]=='Q'){
46             Sum=0;memset(f,0,sizeof(f)); memset(t,true,sizeof(t));
47             dfs(x); sort(f+1,f+Sum+1,cmp);
48             if (Sum<y) printf("-1\n");
49             else printf("%d\n",f[y].y);
50         }
51     }
52     return 0;
53 }
54 //NOIPRP++
View Code

然后正解怎么打呢???

我们需要开N个线段树来维护,但是开N个线段树显然内存会爆炸,所以我们要考虑动态开点

代码就看大佬的吧......

posted @ 2018-11-05 15:25  to_the_end  阅读(248)  评论(0编辑  收藏  举报