2015-9-13 NOIP模拟赛解题报告(by hzwer)
小奇挖矿
「题目背景」
小奇要开采一些矿物,它驾驶着一台带有钻头(初始能力值w)的飞船,按既定路线依次飞过喵星系的n个星球。
「问题描述」
星球分为2类:资源型和维修型。
1.资源型:含矿物质量a[i],若选择开采,则得到a[i]p的金钱,之后钻头损耗k%,即p=p(1-0.01k)
2.维修型:维护费用b[i],若选择维修,则支付b[i]p的金钱,之后钻头修复c%,即p=p(1+0.01c)
(p为钻头当前能力值)
注:维修后钻头的能力值可以超过初始值
请你帮它决策最大化这个收入
「输入格式」
第一行4个整数n,k,c,w。
以下n行,每行2个整数type,x。
type为1则代表其为资源型星球,x为其矿物质含量a[i];
type为2则代表其为维修型星球,x为其维护费用b[i];
「输出格式」
输出一行一个实数(保留两位小数),表示要求的结果。
「样例输入」
5 50 50 10
1 10
1 20
2 10
2 20
1 30
「样例输出」
375.00
「数据范围」
对于30%的数据 n<=100
对于50%的数据 n<=1000,k=100
对于100%的数据 n<=100000,0<=k,c,w,a[i],b[i]<=100
保证答案不超过10^9
动态规划。因为观察到每一个操作对它后面的操作具有影响,根据DP无后效性原则,我们考虑从后往前DP。然后观察到每次操作后面的收益都和p成正比。所以我们设\(dp[i]\)为在第\(i\)个星球且当前\(p=1\)(单位1)的时候最大的收益。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
using namespace std;
double dp[MAXN];
int k,c,n,w;
int name[MAXN],val[MAXN];
int main()
{
scanf("%d%d%d%d",&n,&k,&c,&w);
for(int i=1;i<=n;i++)
scanf("%d%d",&name[i],&val[i]);
double kk=(1-0.01*(double)k);
double cc=(1+0.01*(double)c);
for(int i=n;i>=1;i--)
{
if(name[i]==1) dp[i]=max(dp[i+1],dp[i+1]*kk+val[i]);
else dp[i]=max(dp[i+1],dp[i+1]*cc-val[i]);
}
printf("%.2lf\n",dp[1]*w);
return 0;
}
小奇的数列
「题目背景」
小奇总是在数学课上思考奇怪的问题。
「问题描述」
给定一个长度为n的数列,以及m次询问,每次给出三个数l,r和P,询问 (a[l’] + a[l’+1] + … + a[r’]) mod P的最小值。
其中l <= l’ <= r’ <= r。
即模意义下的区间子串和最小值。
「输入格式」
第一行包含两个正整数n和m,表示数列的长度和询问的个数。
第二行为n个整数,为a[1]..a[n]。
接下来m行,每行三个数l,r和P,代表一次询问。
「输出格式」
对于每次询问,输出一行一个整数表示要求的结果
「样例输入」
4 2
8 15 9 9
1 3 10
1 4 17
「样例输出」
2
1
「数据范围」
对于20%的数据 n<=100,m<=100,p<=200
对于40%的数据 n<=200,m<=1000,p<=500
对于70%的数据 n<=100000,m<=10000,p<=200
对于100%的数据 n<=500000,m<=10000,p<=500,1<=a[i]<=10^9
正解是平衡树。。。但是我不太会。。。这个坑先放这里,,,(然后附上黄学长博文链接:http://hzwer.com/7613.html)
小奇回地球
「题目背景」
开学了,小奇在回地球的路上,遇到了一个棘手的问题。
「问题描述」
简单来说,它要从标号为1的星球到标号为n的星球,某一些星球之间有航线。由于超时空隧道的存在,从一个星球到另一个星球时间可能会倒流,而且,从星球a到b耗费的时间和星球b到a耗费的时间不一定相同。
宇宙法规定:“禁止在出发时间前到达目的地。”
每艘飞船上都有速度调节装置,可以调节飞行的时间。其功能可以使得整次航程中所有两星球间的飞行时间增加或减少相同的整数值。你的任务是帮助它调整速度调节器,找出一条最短时间到达目的地的路径。
「输入格式」
输入文件包含多组数据,第1个数为T,表示数据组数。
对于每组数据,输入第1行为两个正整数n,m,为星球的个数和星球间的路线数。接下来m行,每行三个整数i,j和t,表示由星球i到星球j飞行的时间为t。由i到j最多只会有一条飞行线路。
「输出格式」
输出文件共T行,每组数据输出一行。
如果可以通过调节速度调节器完成任务,则输出一个非负整数,表示由星球1到星球n的最短时间。(注意最短时间要大于或者等于0)。
如果不能由星球1到达星球n,则输出-1。
「样例输入」
1
4 5
1 2 1
1 3 1
2 3 -3
3 1 1
3 4 1
「样例输出」
2
「样例解释」
把速度控制器的值设为1,相当于每个时间值加1,得到的最短路径为1→2→3→4,所需时间为2+(-2)+2=2。
「数据范围」
1,2号测试点,保证所有星球出度不超过1
3,4号测试点,n<=10
5,6号测试点,-100<=t<=100
对于100%的数据T<=10,n<=100,m<=n*(n-1),-100000<=t<=100000
数据随机和构造结合生成
就是枚举出来答案然后判断合不合法。枚举的过程可以使用二分加速。
注意中间会有负环的情况。但是因为不是每个点都会到终点的。所以如果到终点的最短路上没有负环而且从起点到终点的最短路的值大于0即可。
开始先建个反图,使用dfs找一下哪些节点是可以到终点的,打个标记。
我写题的时候zz了,每次更换枚举的增加值的时候还重新建边了。。。。但是其实是可以在spfa跑最短路的时候直接处理的。。这个以后要记住qwq
然后就没有什么了。。。。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define MAXN 100100
using namespace std;
int n,m,edge_number,t,ans;
int u[MAXN],v[MAXN],w[MAXN],len[MAXN];
int dis[MAXN],done[MAXN],head[MAXN],avl[MAXN];
struct Edge{int nxt,to,dis;}edge[MAXN<<1];
inline void add(int from,int to,int dis)
{
edge[++edge_number].dis=dis;
edge[edge_number].to=to;
edge[edge_number].nxt=head[from];
head[from]=edge_number;
}
inline int spfa(int x)
{
queue<int>q;
for(int i=1;i<=n;i++) dis[i]=2147483647,done[i]=0,len[i]=0;
q.push(1); done[1]=1; dis[1]=0; len[1]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
done[u]=0;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(avl[v]==0) continue;
if(dis[v]>dis[u]+edge[i].dis+x)
{
dis[v]=dis[u]+edge[i].dis+x;
if(!done[v])
{
done[v]=1;
len[v]=len[u]+1;
if(len[v]>n)
return -1;
q.push(v);
}
}
}
}
return dis[n];
}
void dfs(int u)
{
avl[u]=1;
for(int i=head[u];i;i=edge[i].nxt)
if(!avl[edge[i].to])
dfs(edge[i].to);
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(head,0,sizeof(head));
memset(avl,0,sizeof(avl));
scanf("%d%d",&n,&m);
int l=-100000,r=100000;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&u[i],&v[i],&w[i]);
for(int i=1;i<=m;i++)
add(v[i],u[i],w[i]);
dfs(n);
memset(head,0,sizeof(head));
edge_number=0;
for(int i=1;i<=m;i++)
add(u[i],v[i],w[i]);
if(!avl[1])
{
printf("-1\n");
continue;
}
while(l<r)
{
int mid=(l+r)>>1;
long long cur=spfa(mid);
if(cur>=0) r=mid,ans=cur;
else l=mid+1;
}
printf("%d\n",ans);
}
return 0;
}
100+40+50=190分收场。。。。真真是蒟蒻被吊打的节奏啊qwq。。。。