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
转移即可

 #include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
const int MAXN = + ;
const int INF = 0x3f3f3f3f;
inline void read(long long &x)
{
x = ;char ch = getchar(), c = ch;
while(ch < '' || ch > '')c = ch, ch = getchar();
while(ch <= '' && ch >= '')x = x * + ch - '', ch = getchar();
if(c == '-')x = -x;
}
long long n,W,v[MAXN],w[MAXN],dp[][MAXN][MAXN * ],mi,ans,sum;
int main()
{
read(n), read(W);
mi = INF;
for(register int i = ;i <= n;++ i) read(v[i]), read(w[i]), mi = min(mi, w[i]);
for(register int i = ;i <= n;++ i) w[i] -= mi, sum += w[i];
int p = ;
for(register long long i = ;i <= n;++ i, p ^= )
for(register long long j = ;j <= i;++ j)
for(register long long k = ;k <= sum;++ k)
{
dp[p][j][k] = dp[p ^ ][j][k];
if(j*mi + k <= W && k >= w[i])
{
dp[p][j][k] = max(dp[p][j][k], dp[p ^ ][j - ][k - w[i]] + v[i]);
ans = max(ans, dp[p][j][k]);
}
}
printf("%d", ans);
return ;
}

T1

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,则不需修改

 #include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define max(a, b) ((a) > (b) ? (a) : (b)) const long long MAXN = + ; inline void read(long long &x)
{
x = ;char ch = getchar(), c = ch;
while(ch < '' || ch > '')c = ch, ch = getchar();
while(ch <= '' && ch >= '')x = x * + ch - '', ch = getchar();
if(c == '-')x = -x;
} struct Edge
{
long long u,v,next;
Edge(long long _u, long long _v, long long _next){u = _u;v = _v;next = _next;}
Edge(){}
}edge[MAXN << ]; long long head[MAXN], cnt; void insert(long long a, long long b)
{
edge[++cnt] = Edge(a, b, head[a]);
head[a] = cnt;
} long long value[MAXN],dp1[MAXN],dp2[MAXN],n,b[MAXN]; void dfs(long long u)
{
b[u] = ;
for(register long long pos = head[u];pos;pos = edge[pos].next)
{
long long v = edge[pos].v;
if(b[v])continue;
dfs(v);
dp1[u] = max(dp1[u], dp1[v]);
dp2[u] = max(dp2[u], dp2[v]);
}
long long tmp = value[u] + dp1[u] - dp2[u];
if(tmp > )dp2[u] += tmp;
else dp1[u] -= tmp;
return;
} int main()
{
read(n);
register long long tmp1, tmp2;
for(register long long i = ;i <= n;++ i) read(value[i]);
for(register long long i = ;i < n;++ i) read(tmp1), read(tmp2), insert(tmp1, tmp2), insert(tmp2, tmp1);
dfs();
printf("%lld", dp1[] + dp2[]);
return ;
}

T2

简明易懂的序列
【题目描述】
为了体现出题人的友善以及照应题目名称,我们将题意浓缩为一句话
求给定长度为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也可以手写

——张浩南

我这里为了方便处理,数据是倒着读的,这样就改成了求“严格单调递增”了。

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define max(a, b) ((a) > (b) ? (a) : (b)) inline void read(long long &x)
{
x = ;char ch = getchar(),c = ch;
while(ch < '' || ch > '')c = ch, ch = getchar();
while(ch <= '' && ch >= '')x = x * + ch - '', ch = getchar();
if(c == '-')x = -x;
} const long long MAXN = + ;
const long long MOD = ;
long long data[MAXN],cnt,pre[MAXN],next[MAXN];
struct Queue
{
long long size,head,tail;
void push_back(long long x)
{
data[++cnt] = x;
if(!size)
{
head = tail = cnt;
pre[tail] = head;
next[head] = tail;
}
else
{
pre[cnt] = tail;
next[tail] = cnt;
tail = cnt;
}
++ size;
}
void push_front(long long x)
{
data[++cnt] = x;
if(!size)
{
head = tail = cnt;
pre[tail] = head;
next[head] = tail;
}
else
{
pre[head] = cnt;
next[cnt] = head;
head = cnt;
}
++ size;
}
long long pop_back()
{
long long re = data[tail];
if(size > )
{
tail = pre[tail];
next[tail] = ;
}
else
head = tail = ;
-- size;
return re;
}
long long pop_front()
{
long long re = data[head];
if(size > )
{
head = next[head];
pre[head] = ;
}
else
head = tail = ;
-- size;
return re;
}
}q[MAXN]; //严格递减转严格递增,不用变负数 long long ans1, ans2,n,num[MAXN],dp[MAXN],f[MAXN],g[MAXN],sum[MAXN]; int main()
{
read(n);
for(register long long i = n;i >= ;-- i) read(num[i]);
memset(f, 0x3f, sizeof(f));
for(register long long i = ;i <= n;++ i)
{
f[dp[i] = std::lower_bound(f + , f + + n, num[i]) - f] = num[i];
ans1 = max(ans1, dp[i]);
}
//在每个f[i]下面挂一个队列,这个队列显然是单调不增的。
//每访问到一个元素i,把所有f[i]-1所在队列中队首大于等于它的全部删除,并更新其方案数
// 然后,将f[i]所在队列队尾跟它相等的删除
register long long x = ;
for(register long long i = ;i <= n;++ i)
{
if(dp[i] == )g[i] = ;
else
{
x = dp[i] - ;
while(q[x].size &&(num[data[q[x].head]] >= num[i]))sum[x] = (sum[x] - g[data[q[x].pop_front()]] + MOD) % MOD;
g[i] = sum[x]%MOD;
}
x = dp[i];
while(q[x].size && (num[data[q[x].tail]] == num[i]))sum[x] = (sum[x] - g[data[q[x].pop_back()]] + MOD)%MOD;
q[x].push_back(i);
sum[x] = (sum[x] + g[i]) % MOD;
}
long long tmp = ;
for(register long long i = n;i >= ;-- i)
if(dp[i] == ans1 && tmp != num[i])
ans2 = (ans2 + g[i])%MOD, tmp = num[i];
printf("%lld %lld", ans1, ans2%MOD);
return ;
}

T3

顺便给标称的代码

 #include<cstdio>
#include<algorithm>
#define M 300005
#define mo 1000000007
using namespace std;
int n;
int a[M],f[M],mi[M],g[M],sum[M],pre[M],sub[M];
struct node{
int siz,head,tail;
int front(){return head;}
int back(){return tail;}
bool empty(){return siz==;}
void push_back(int x)
{
if (siz)
{
int y=tail;
pre[x]=y;
sub[y]=x;
}
tail=x;
if (siz==) head=x;
++siz;
}
void pop_back()
{
if (siz>)
{
int y=pre[tail];
sub[y]=;
tail=y;
}
else head=tail=;
--siz;
}
void pop_front()
{
if (siz>)
{
int y=sub[head];
pre[y]=;
head=y;
}
else head=tail=;
--siz;
}
}q[M];
int in()
{
int t=;char ch=getchar();bool f=;
while (ch<''||ch>'') f|=(ch=='-'),ch=getchar();
while (ch>=''&&ch<='') t=t*+(ch^),ch=getchar();
return f?-t:t;
}
int up(int x,int y)
{
x+=y;
return x<?x+mo:(x>=mo?x-mo:x);
}
main()
{
freopen("data.txt","r",stdin);
n=in();
for (int i=;i<=n;++i) a[i]=in();
int len=;
for (int x,i=;i<=n;++i)
{
x=lower_bound(mi+,mi+len+,-a[i])-mi;
mi[x]=-a[i];
if (x>len) len=x;
f[i]=x;
}
for(int x,i=;i<=n;++i)
{
if (f[i]==) g[i]=;
else
{
x=f[i]-;
while (!q[x].empty()&&a[q[x].front()]<=a[i]) sum[x]=up(sum[x],-g[q[x].front()]),q[x].pop_front();
g[i]=sum[x];
}
x=f[i];
while (!q[x].empty()&&a[q[x].back()]==a[i]) sum[x]=up(sum[x],-g[q[x].back()]),q[x].pop_back();
q[x].push_back(i);
sum[x]=up(sum[x],g[i]);
}
int t=1e9,ans=;
for (int i=n;i;--i)
if (f[i]==len&&t>a[i])
ans=up(ans,g[i]),
t=a[i];
printf("%d %d\n",len,ans);
}

zhn T3 std

NOIP模拟 17.8.18的更多相关文章

  1. NOIP模拟17.9.22

    NOIP模拟17.9.22 前进![问题描述]数轴的原点上有一只青蛙.青蛙要跳到数轴上≥

  2. NOIP模拟17.9.21

    NOIP模拟17.9.21 3 58 145 201 161.5 样例输出21.6 数据规模及约定对于40% 的数据,N <= 20对于60% 的数据,N <= 1000对于100% 的数 ...

  3. NOIP模拟 17.8.20

    NOIP模拟17.8.20 A.阶乘[题目描述]亲爱的xyx同学正在研究数学与阶乘的关系,但是他喜欢颓废,于是他就制作了一个和阶乘有关系的数学游戏:给出两个整数 n,m,令 t = !n,每轮游戏的流 ...

  4. NOIP模拟 17.8.15

    NOIP模拟17.8.15 A 债务文件名 输入文件 输出文件 时间限制 空间限制debt.pas/c/cpp debt.in debt.out 1s 128MB[题目描述]小 G 有一群好朋友,他们 ...

  5. NOIP模拟 17.8.17

    NOIP模拟17.8.17 A 小 G 的字符串文件名 输入文件 输出文件 时间限制 空间限制str.pas/c/cpp str.in str.out 1s 128MB[题目描述]有一天,小 L 给小 ...

  6. NOIP模拟 17.8.16

    NOIP模拟17.8.16 A 债务文件名 输入文件 输出文件 时间限制 空间限制debt.pas/c/cpp debt.in debt.out 1s 128MB[题目描述]小 G 有一群好朋友,他们 ...

  7. NOIP模拟 17.8.14

    NOIP模拟17.8.14 (天宇哥哥考察细心程度的题) [样例解释]如果删去第一个 1:在[3,1,2]中有 3 个不同的数如果删去 3:在[1,1,2]中有 2 个不同的数如果删去第二个 1:在[ ...

  8. NOIP模拟 17.9.28

    公交车[问题描述]市内有

  9. Noip模拟17 2021.7.16

    我愿称这场考试为STL专练 T1 世界线 巧妙使用$bitset$当作vis数组使用,内存不会炸,操作还方便,的确是极好的. 但是这个题如果不开一半的$bitset$是会炸内存的,因为他能开得很大,但 ...

随机推荐

  1. 2018-8-10-dotnet-从入门到放弃的-500-篇文章合集

    title author date CreateTime categories dotnet 从入门到放弃的 500 篇文章合集 lindexi 2018-08-10 19:16:52 +0800 2 ...

  2. mysql三表联合查询,结果集合并

    参考: mysql 结果集去重复值并合并成一行 SQL 三表联查 数据库三表连接查询怎么做 合并: MySQL中group_concat函数 完整的语法如下: group_concat([DISTIN ...

  3. Mybatis的插件 PageHelper 分页查询使用方法

    参考:https://blog.csdn.net/ckc_666/article/details/79257028 Mybatis的一个插件,PageHelper,非常方便mybatis分页查询,国内 ...

  4. 前端在本地启动服务预览html页面

    在开发移动端项目时浏览器里出来的效果往往到真机上和预想的有出入,在开发过程中知道了一个可以在本地自己启动一个服务器在手机预览的办法. 1.首先在终端安装http. npm i http-server ...

  5. js身份证号码验证(小程序版)

    参考知乎专栏文章https://zhuanlan.zhihu.com/p/22949023 <view class='bgw'> <form> ...... <view ...

  6. vim用户设置

    此配置目前使用户mac,linux,win,但是win系统需要提前配置mingw32相关的gcc系统路径等信息. " Setting some decent VIM settings for ...

  7. c++ 链接mysql:error LNK2019: 无法解析的外部符号

    使用VS2012编译项目报错如下: error LNK2019: 无法解析的外部符号 _mysql_real_connect@32,该符号在函数 _main 中被引用 error LNK2019: 无 ...

  8. kuangbin带我飞QAQ 线段树

    1. HDU1166 裸线段树点修改 #include <iostream> #include <string.h> #include <cstdio> #incl ...

  9. bzoj 1053 [HAOI2007]反素数ant——关于质数的dfs / 打表

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1053 写了个打表程序. #include<iostream> #include& ...

  10. pc端拖拽

    var move=document.getElementsByClassName("page1_2")[0]; var startX=0; var startY=0; var x= ...