NOIP模拟 17.8.18
Posted 嘒彼小星
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP模拟 17.8.18相关的知识,希望对你有一定的参考价值。
NOIP模拟17.8.18
A.小菜一碟的背包
【题目描述】
Blice和阿强巴是好朋友
但萌萌哒Blice不擅长数学,所以阿强巴给了她一些奶牛做练习
阿强巴有 n头奶牛,每头奶牛每天可以产一定量的奶,同时也需要一定量的草作为饲料
对于第 i头奶牛来说,它每天可以产 vi升的奶,同时需要 wi千克的草作为饲料
现在来自蚯蚓国的九条可怜想借一些奶牛,使借走的这些奶牛每天的总产奶量最大,但九条可怜很穷,每天最多只能提供W
千克的草作为饲料,而且她还需要对付跳蚤国的神刀手,所以她把这个问题交给了阿强巴,不不不……阿强巴觉得这个
问题太简单了,所以把这个问题交给了你
【输入格式】
第一行两个整数 n,W,表示奶牛的数量和每天最多能提供的草
接下来 n行,每行两个整数,第 行表示第vi 头奶牛的产奶量 和食量wi
【输出格式】
仅一行,输出一个整数,表示每天最大的总产奶量
【输入样例】
8 40
10 9
12 11
11 12
10 10
8 11
7 9
8 10
9 10
【输出样例】
41
【限制与约定】
对于 20 %的数据,n<=10
另有 30%的数据,W<=10000
另有 10%的数据,wi = w1
对于 100%的数据,1<=n<=100,1<=wi,W<=1e9,1<=vi<=1e7
对于所有数据,均满足特殊限制:w1<=wi<=w1+3
【题解】
此题有一个十分强大的限制:对于所有数据,w1<=wi<=w1+3
这也就意味着,物体体积只有0,1,2,3三种。
但是,如何让他们满足限制W?我们知道,如果只选一种物品,那么体积限制是W-w1;选两种物品,体积限制是W-w1*2.。。。
所以,加一维,表示已经选的物品个数即可
f[i][j][k]表示DP到第i个奶牛时,选了j个奶牛,这j个奶牛(减去base之后的)w_i的和为j
转移即可
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #define min(a, b) ((a) < (b) ? (a) : (b)) 6 #define max(a, b) ((a) > (b) ? (a) : (b)) 7 const int MAXN = 250 + 10; 8 const int INF = 0x3f3f3f3f; 9 inline void read(long long &x) 10 { 11 x = 0;char ch = getchar(), c = ch; 12 while(ch < ‘0‘ || ch > ‘9‘)c = ch, ch = getchar(); 13 while(ch <= ‘9‘ && ch >= ‘0‘)x = x * 10 + ch - ‘0‘, ch = getchar(); 14 if(c == ‘-‘)x = -x; 15 } 16 long long n,W,v[MAXN],w[MAXN],dp[2][MAXN][MAXN * 10],mi,ans,sum; 17 int main() 18 { 19 read(n), read(W); 20 mi = INF; 21 for(register int i = 1;i <= n;++ i) read(v[i]), read(w[i]), mi = min(mi, w[i]); 22 for(register int i = 1;i <= n;++ i) w[i] -= mi, sum += w[i]; 23 int p = 0; 24 for(register long long i = 1;i <= n;++ i, p ^= 1) 25 for(register long long j = 1;j <= i;++ j) 26 for(register long long k = 0;k <= sum;++ k) 27 { 28 dp[p][j][k] = dp[p ^ 1][j][k]; 29 if(j*mi + k <= W && k >= w[i]) 30 { 31 dp[p][j][k] = max(dp[p][j][k], dp[p ^ 1][j - 1][k - w[i]] + v[i]); 32 ans = max(ans, dp[p][j][k]); 33 } 34 } 35 printf("%d", ans); 36 return 0; 37 }
B.整齐划一的子树
【题目描述】
蛐蛐国王讨厌冗长的题面,所以他要把题面写的简单一点。
给定一棵 n个节点的树,树上的每个节点 i有一个权值 ai,每次操作中你可以选择一个包含1号节点的连通子树,将这个连
通子树上所有节点的权值加上(或减去)一个相同的非负整数,要求将所有节点变为0,且最小化所有加上(或减去)的
非负整数之和
【输入描述】
第一行一个整数 n,表示这棵树的节点个数。
第二行 n个整数,第 i个整数表示节点i 上的权值 ai。
接下来共n-1 行,每行两个整数ui,vi ,表示第 条边上连接ui,vi 这两个顶点。
【输出格式】
仅一行一个整数,表示最小答案。
【样例输入】
3
2 ‐2 2
1 2
1 3
【样例输出】
6
【样例解释】
第一次选择节点 构成的联通子树,权值都加上2;
第二次选择节点 构成的联通子树,权值都减去2;
第三次选择节点 ,权值减去2。
【限制与约定】
对于 30% 的数据,n ≤ 1000
对于另外15% 的数据,ui = i, vi = i + 1(1 ≤ i ≤ n − 1)
对于另外15% 的数据,ui = 1, vi = i(2 ≤ i ≤ n)
对于 的数据100%, n ≤ 300000 , |ai| ≤ 1e9,ui, vi ≤ n ,保证给定数据是一棵树
提示
设原树点集为 V,边集为 W,联通子树定义为选择子集 S ⊆ V,使得对于任意一对点 u,
存在边u, v ∈ S(u ≠ v) e(u, v) ∈ E
通俗来说,就是在原树中选择一些点,使得这些点所发出的边仍能将它们连通起来。
由于输入文件可能较大,所以请使用scanf或其他更快速的方法进行读入操作
【题解】
这是一道很裸的树形DP题,虽然我们做出来。
递归的想,要先把叶子节点变为0,再把他们的父亲变0,再...
dp1[i]表示把i和他的子树变为0的最小累加值
dp1[i]表示把i和他的子树变为0的最小累减值
转移:dp1[i] = max(dp1[i], dp1[son]) dp2[i] = max(dp2[i], dp2[son])
为什么取max?我们可以把操作想成每次只能+1或-1。需要做的次数决定于最大的那个,
最小的那个可以少做几次
这样就得到节点i的累加累减值,操作后i节点权值变为:t = value[i] - dp2[i] + dp1[i]
若t > 0,则还需减少t,dp2[i] += t
若t < 0,则还需增加t,dp1[i] -= t,注意t是负数
若t = 0,则不需修改
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #define max(a, b) ((a) > (b) ? (a) : (b)) 6 7 const long long MAXN = 400000 + 10; 8 9 inline void read(long long &x) 10 { 11 x = 0;char ch = getchar(), c = ch; 12 while(ch < ‘0‘ || ch > ‘9‘)c = ch, ch = getchar(); 13 while(ch <= ‘9‘ && ch >= ‘0‘)x = x * 10 + ch - ‘0‘, ch = getchar(); 14 if(c == ‘-‘)x = -x; 15 } 16 17 struct Edge 18 { 19 long long u,v,next; 20 Edge(long long _u, long long _v, long long _next){u = _u;v = _v;next = _next;} 21 Edge(){} 22 }edge[MAXN << 1]; 23 24 long long head[MAXN], cnt; 25 26 void insert(long long a, long long b) 27 { 28 edge[++cnt] = Edge(a, b, head[a]); 29 head[a] = cnt; 30 } 31 32 long long value[MAXN],dp1[MAXN],dp2[MAXN],n,b[MAXN]; 33 34 void dfs(long long u) 35 { 36 b[u] = 1; 37 for(register long long pos = head[u];pos;pos = edge[pos].next) 38 { 39 long long v = edge[pos].v; 40 if(b[v])continue; 41 dfs(v); 42 dp1[u] = max(dp1[u], dp1[v]); 43 dp2[u] = max(dp2[u], dp2[v]); 44 } 45 long long tmp = value[u] + dp1[u] - dp2[u]; 46 if(tmp > 0)dp2[u] += tmp; 47 else dp1[u] -= tmp; 48 return; 49 } 50 51 int main() 52 { 53 read(n); 54 register long long tmp1, tmp2; 55 for(register long long i = 1;i <= n;++ i) read(value[i]); 56 for(register long long i = 1;i < n;++ i) read(tmp1), read(tmp2), insert(tmp1, tmp2), insert(tmp2, tmp1); 57 dfs(1); 58 printf("%lld", dp1[1] + dp2[1]); 59 return 0; 60 }
简明易懂的序列
【题目描述】
为了体现出题人的友善以及照应题目名称,我们将题意浓缩为一句话
求给定长度为n 的数字序列a 中最长严格下降子序列的长度以及符合该长度且本质不同的严格下降子序列个数
【输入描述】
第一行一个整数 n,表示序列的长度
第二行共 n个整数,第i 个数表示ai
【输出描述】
仅一行,共两个整数,分别表示最长严格下降子序列的长度和符合条件的严格下降子序列个数,两数间用一个空格隔开
由于符合该长度的严格下降子序列个数可能很多,你只需要输出它对1000000007 取模后的结果
【输入样例1】
4
2 1 100 99 100 99
【输出样例1】
2 2
【输出样例2】
10
5 8 7 11 2 4 9 1 3 7
【输出样例2】
4 3
【样例解释】
样例1中最长严格下降子序列长度为 2,符合条件的严格下降子序列为{2,1} 和{100,99}
样例2中最长严格下降子序列长度为 4,符合条件的严格下降子序列为{8,7,2,1} 和 {8,7,4,3} 和{8,7,4,1}
【限制与约定】
对于 20%的数据, n ≤ 10
对于 40%的数据, n ≤ 4000
另有20% 的数据,保证只有一个严格下降子序列符合条件
对于100% 的数据, 1 ≤ n,ai ≤ 300000
【提示】
子序列指在原序列 a中选出一些元素,按原位置关系不变而形成的新的序列b
当一个长度为 n的序列 a符合a1 > a2 > a3 >...> an ,我们称序列 a是严格下降的
【题解】
第一问nlogn做法不在多言。用f[i]表示长度为i的最后一个元素的最大值
第二问,在f[i]的每一个i上维护一个出现过的值得队列,可知这个队列是单调非减的。
g[i]表示以队列中q[i]这个元素结尾的最长严格下降子序列个数
假如f[j]=k-1,f[i]=k,j<i,但a[j]<=a[i],显然j不能作为i的转移点,那么对于比i大的那
些i’(f[i’]=k),j更不可能是i’的转移点了,因为a[i’]>=a[i]
q[i]存储f[]=i的点
我们在转移f[i]时,去寻找q[f[i]-1]的队首,如果队首head的a[head]<=a[i],那就弹出队首,循环该过程直到队为空或a[head]>a[i]
转移完成后把i压入q[f[i]]队尾即可
再开个数组记录每个队列中g[i]的和即可
到此问题并没有完成,因为没有考虑a[i]重复的情况,即方案数计算可能重复
当j<k,a[j]=a[k],f[j]=f[k]时,我们肯定要从k转移,因为k在j后面,所以g[k]>=g[j]
所以我们要保证每个队列中的点a[i]单调递增,而不是单调不降
把i压入相应队列时,还要把那些和a[i]相等的队尾元素弹出
开一个双端队列即可,可以用STL的deque也可以手写
——张浩南
我这里为了方便处理,数据是倒着读的,这样就改成了求“严格单调递增”了。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 #define max(a, b) ((a) > (b) ? (a) : (b)) 7 8 inline void read(long long &x) 9 { 10 x = 0;char ch = getchar(),c = ch; 11 while(ch < ‘0‘ || ch > ‘9‘)c = ch, ch = getchar(); 12 while(ch <= ‘9‘ && ch >= ‘0‘)x = x * 10 + ch - ‘0‘, ch = getchar(); 13 if(c == ‘-‘)x = -x; 14 } 15 16 const long long MAXN = 4000000 + 10; 17 const long long MOD = 1000000007; 18 long long data[MAXN],cnt,pre[MAXN],next[MAXN]; 19 struct Queue 20 { 21 long long size,head,tail; 22 void push_back(long long x) 23 { 24 data[++cnt] = x; 25 if(!size) 26 { 27 head = tail = cnt; 28 pre[tail] = head; 29 next[head] = tail; 30 } 31 else 32 { 33 pre[cnt] = tail; 34 next[tail] = cnt; 35 tail = cnt; 36 } 37 ++ size; 38 } 39 void push_front(long long x) 40 { 41 data[++cnt] = x; 42 if(!size) 43 { 44 head = tail = cnt; 45 pre[tail] = head; 46 next[head] = tail; 47 } 48 else 49 { 50 pre[head] = cnt; 51 next[cnt] = head; 52 head = cnt; 53 } 54 ++ size; 55 } 56 long long pop_back() 57 { 58 long long re = data[tail]; 59 if(size > 1) 60 { 61 tail = pre[tail]; 62 next[tail] = 0; 63 } 64 else 65 head = tail = 0; 66 -- size; 67 return re; 68 } 69 long long pop_front() 70 { 71 long long re = data[head]; 72 if(size > 1) 73 { 74 head = next[head]; 75 pre[head] = 0; 76 } 77 else 78 head = tail = 0; 79 -- size; 80 return re; 81 } 82 }q[MAXN]; 83 84 //严格递减转严格递增,不用变负数 85 86 long long ans1, ans2,n,num[MAXN],dp[MAXN],f[MAXN],g[MAXN],sum[MAXN]; 87 88 int main() 89 { 90 read(n); 91 for(register long long i = n;i >= 1;-- i) read(num[i]); 92 memset(f, 0x3f, sizeof(f)); 93 for(register long long i = 1;i <= n;++ i) 94 { 95 f[dp[i] = std::lower_bound(f + 1, f + 1 + n, num[i]) - f] = num[i]; 96 ans1 = max(ans1, dp[i]); 97 } 98 //在每个f[i]下面挂一个队列,这个队列显然是单调不增的。 99 //每访问到一个元素i,把所有f[i]-1所在队列中队首大于等于它的全部删除,并更新其方案数 100 // 然后,将f[i]所在队列队尾跟它相等的删除 101 register long long x = 0; 102 for(register long long i = 1;i <= n;++ i) 103 { 104 if(dp[i] == 1)g[i] = 1; 105 else 106 { 107 x = dp[i] - 1; 108 while(q[x].size &&(num[data[q[x].head]] >= num[i]))sum[x] = (sum[x] - g[data[q[x].pop_front()]] + MOD) % MOD; 109 g[i] = sum[x]%MOD; 110 } 111 x = dp[i]; 112 while(q[x].size && (num[data[q[x].tail]] == num[i]))sum[x] = (sum[x] - g[data[q[x].pop_back()]] + MOD)%MOD; 113 q[x].push_back(i); 114 sum[x] = (sum[x] + g[i]) % MOD; 115 } 116 long long tmp = 0; 117 for(register long long i = n;i >= 1;-- i) 118 if(dp[i] == ans1 && tmp != num[i]) 119 ans2 = (ans2 + g[i])%MOD, tmp = num[i]; 120 printf("%lld %lld", ans1, ans2%MOD); 121 return 0; 122 }
顺便给标称的代码
1 #include<cstdio> 2 #include<algorithm> 3 #define M 300005 4 #define mo 1000000007 5 using namespace std; 6 int n; 7 int a[M],f[M],mi[M],g[M],sum[M],pre[M],sub[M]; 8 struct node{ 9 int siz,head,tail; 10 int front(){return head;} 11 int back(){return tail;} 12 bool empty(){return siz==0;} 13 void push_back(int x) 14 { 15 if (siz) 16 { 17 int y=tail; 18 pre[x]=y; 19 sub[y]=x; 20 } 21 tail=x; 22 if (siz==0) head=x; 23 ++siz; 24 } 25 void pop_back() 26 { 27 if (siz>1) 28 { 29 int y=pre[tail]; 30 sub[y]=0; 31 tail=y; 32 } 33 else head=tail=0; 34 --siz; 35 } 36 void pop_front() 37 { 38 if (siz>1) 39 { 40 int y=sub[head]; 41 pre[y]=0; 42 head=y; 43 } 44 else head=tail=0; 45 --siz; 46 } 47 }q[M]; 48 int in() 49 { 50 int t=0;char ch=getchar();bool f=0; 51 while (ch<‘0‘||ch>‘9‘) f|=(ch==‘-‘),ch=getchar(); 52 while (ch>=‘0‘&&ch<=‘9‘) t=t*10+(ch^48),ch=getchar(); 53 return f?-t:t; 54 } 55 int up(int x,int y) 56 { 57 x+=y; 58 return x<0?x+mo:(x>=mo?x-mo:x); 59 } 60 main() 61 { 62 freopen("data.txt","r",stdin); 63 n=in(); 64 for (int i=1;i<=n;++i) a[i]=in(); 65 int len=0; 66 for (int x,i=1;i<=n;++i) 67 { 68 x=lower_bound(mi+1,mi+len+1,-a[i])-mi; 69 mi[x]=-a[i]; 70 if (x>len) len=x; 71 f[i]=x; 72 } 73 for(int x,i=1;i<=n;++i) 74 { 75 if (f[i]==1) g[i]=1; 76 else 77 { 78 x=f[i]-1; 79 while (!q[x].empty()&&a[q[x].front()]<=a[i]) sum[x]=up(sum[x],-g[q[x].front()]),q[x].pop_front(); 80 g[i]=sum[x]; 81 } 82 x=f[i]; 83 while (!q[x].empty()&&a[q[x].back()]==a[i]) sum[x]=up(sum[x],-g[q[x].back()]),q[x].pop_back(); 84 q[x].push_back(i); 85 sum[x]=up(sum[x],g[i]); 86 } 87 int t=1e9,ans=0; 88 for (int i=n;i;--i) 89 if (f[i]==len&&t>a[i]) 90 ans=up(ans,g[i]), 91 t=a[i]; 92 printf("%d %d\n",len,ans); 93 }
以上是关于NOIP模拟 17.8.18的主要内容,如果未能解决你的问题,请参考以下文章