前言
这次考试据说有点难度,果然有些题目的确看着有些头晕,不过整体而言还算好吧。
题目
1.我不想写背景(wtf.pas/c/cpp)
【题目描述】
某巨魔去滑雪(没滑雪板),但他的技术并不精湛,在滑雪场里,每天会提供S门滑雪课。第i节课始于时间Mi,上课的时长为Li(只有在Mi时刻才能选择去上第i节课,其他时间不能选择上第i节课)。上完第i节课后,巨魔的滑雪能力会变成Ai. (注意:这个能力是绝对的,不是能力的增长值)。 巨魔买了一张地图,地图上显示了N个可供滑雪的斜坡,从第i个斜坡的顶端滑至底部所需的时长Di,以及每个斜坡所需要的滑雪能力Ci,以保证滑雪的安全性。巨魔的能力必须大于等于这个等级,以使得他能够安全滑下。 巨魔可以用他的时间来滑雪,上课,或者在旁边菊花丛中练习箭法,但是他必须在T时刻离开滑雪场。这意味着他必须在T时刻之前(或者T时刻)完成最后一次滑雪(或者上课)。 求巨魔在时间内最多可以完成多少次滑雪。这一天开始的时候,他的滑雪能力为1。
【输入格式】
第一行3个数字,T、S、N。
接下来S行,每行3个数字Mi、Li、Ai。
接下来N行,每行2个数字Ci、Di。
【输出格式】
一个整数,表示巨魔滑雪的最大次数。
【样例输入输出】
wtf.in
10 1 2
3 2 5
4 1
1 3
wtf.out
6
【样例解释】
0时刻,选择在第1个斜坡上滑雪,时间花费3。
3时刻,选择上第1节课。滑雪技术提高到5,时间花费2。
5时刻,选择在第2个斜坡上滑雪,时间花费1。
6时刻,选择在第2个斜坡上滑雪,时间花费1。
7时刻,选择在第2个斜坡上滑雪,时间花费1。
8时刻,选择在第2个斜坡上滑雪,时间花费1。
9时刻,选择在第2个斜坡上滑雪,时间花费1。
10时刻,收队了。
总滑雪次数:6
【数据范围】
50%的数据:
1≤N,T≤1000
。
100%的数据:
1≤N,T≤10000
、
1≤S,Ai,Ci≤100
、
1≤Mi,Li,Di≤10000
。
【题解】
原题在此:P2948 [USACO09OPEN]滑雪课Ski Lessons
我们可以直接令
f(t,k)
表示当前时间为 t,技能为k 还能滑的最多次数,状态转移:
f(t.k)=max{f(t+1,k)max{f(Mi+Li)(1≤i≤s,t=Mi)}max{f(t+Di,k)(1≤i≤n,k≥ci)}
然后发现还是会超时,所以就要进行优化操作,我们可以发现提前把第i个培训记录到 t=Mi 处,状态转移只需要访问对应时间处的链表。 预处理随着技能k 的变化,滑一次需要的最短时间.
所以我们可以得到
f(t.k)=max{f(t+1,k)max{f(Mi+Li)(1≤i≤s,t=Mi)}1+f(t+Pk,k)
然后就不会超时了。
【代码】
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
inline int read() {
int in=0,f=1;
char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())
if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar ())
in=in*10+ch-'0';
return in*f;
}
const int maxn = 10000+10;
const int maxs = 100+10;
struct Cr {
int x,y,lv;
}cource[maxs];
struct Node {
int lv,w;
}node[maxn];
int t,n,s,Lv;
int f[maxn],w[maxn],lv[maxn];
int ff[maxs][maxn];
int rec[maxs][2];
namespace myself {
bool cmp(const Node &a,const Node &b) {
return a.lv<b.lv;
}
bool cmp2(const Cr &a,const Cr &b) {
return a.x<b.x;
}
inline void pre() {
std::sort(node+1,node+n+1,cmp);
rec[node[1].lv][0]=1;
for(int i=1;i<=n;i++) {
while(node[i].lv==node[i+1].lv) {
i++;
}
rec[node[i].lv][1]=i;rec[node[i+1].lv][0]=i+1;
}
}
inline void work1() {
for(int k=1;k<=Lv;k++) {
if(rec[k][0]!=rec[k][1] or rec[k][0]) {
for(int i=rec[k][0];i<=rec[k][1];i++)
for(int j=node[i].w;j<=t;j++)
f[j]=Max(f[j],f[j-node[i].w]+1);
}
for(int i=1;i<=t;i++)
ff[k][i]=f[i];
}
}
inline void work2() {
work1();
memset(f,0,sizeof f);
std::sort(cource+1,cource+s+1,cmp2);
cource[s+1].x=t;cource[0].y=0;cource[0].lv=1;
for(int i=s;i>=0;i--) {
for(int j=i+1;j<=s+1;j++) {
if(cource[j].x-cource[i].x-cource[i].y>=0)
f[i]=Max(f[i],f[j]+ff[cource[i].lv][cource[j].x-cource[i].x-cource[i].y]);
}
}
printf("%d\n",f[0]);
}
}
int main() {
freopen("wtf.in","r",stdin);
freopen("wtf.out","w",stdout);
t=read();s=read();n=read();
for(int i=1;i<=s;i++) {
cource[i].x=read();cource[i].y=read();
cource[i].lv=read();Lv=Max(Lv,cource[i].lv);
}
for(int i=1;i<=n;i++) {
node[i].lv=read();node[i].w=read();
}
myself::pre();
myself::work2();
return 0;
}
2.我真不想写背景(wth.pas/c/cpp)
【题目描述】
某巨魔突然对等式很感兴趣,他正在研究 a1x1+a2x2+…+anxn=B 存在非负整
数解的条件,他要求你编写一个程序,给定 N、{an}、以及 B 的取值范围,求出
有多少 B 可以使等式存在非负整数解。
【输入格式】
输入的第一行包含 3 个正整数,分别表示 N、BMin、BMax 分别表示数列的
长度、B 的下界、B 的上界。
输入的第二行包含 N 个整数,即数列{an}的值。
【输出格式】
输出一个整数,表示有多少 B 可以使等式存在非负整数解。
【样例输入输出】
wth.in
2 5 10
3 5
wth.out
5
【样例解释】
对于 B=5,式子有 x1=0,x2=1。
对于 B=6,式子有 x1=2,x2=0。
对于 B=7,无解。
对于 B=8,式子有 x1=1,x2=1。
对于 B=9,式子有 x1=3,x2=0。
对于 B=10,式子有 x1=0,x2=2。
【数据范围】
20%的数据,
N≤5
,
1≤BMin≤BMax≤10
。
40%的数据,
N≤10
,
1≤BMin≤BMax≤106
。
100%的数据,
N≤12
,
0≤ai≤4∗105
,
1≤BMin≤BMax≤1012
。
【题解】
原题:bzoj2118 墨墨的等式
40分:
由于b的数量较小,这就和我们平时使用的动态规划算法没有什么区别,这就是的完全背包问题,动态规划方程:
F[i,j]=F[i−1,j−ai]orF[i,j]
100分:
找一个ai,若x为合法的B,则x+ai也合法
设bi为最小的x,满足x mod mn = i
求出每个bi就可以求答案了
bi用最短路求就好了啊 意会一下
最后枚举余数搞一下就算出答案了
详细一点的解释可以看LCY大神的blog
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=500002;
inline LL read(){
char ch=getchar(); LL x=0;
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0'; ch=getchar();}
return x;
}
struct g{
LL w;int x;
bool operator <(g a)const{return w>a.w;}
};
struct u{int y;LL c;int next;}a[12*N]; int len,first[N];
void ins(int x,int y,LL c){a[++len]=(u){y,c,first[x]},first[x]=len;}
int o[13]; LL d[N];
priority_queue<g>q;
int main()
{
int n=read(),i,j,mn=1e6; LL b1=read(),b2=read();
for(i=1;i<=n;i++)o[i]=read(),mn=o[i]<mn?o[i]:mn;
for(i=1;i<mn;i++)d[i]=1e15;
for(i=1;i<=n;i++)
if(o[i]%mn){
int _=o[i]%mn;
for(j=0;j<mn;j++)ins(j,(j+_)%mn,o[i]);
}
q.push((g){0,0});
while(!q.empty()){
g r=q.top(); q.pop(); int x=r.x;
if(r.w!=d[x])continue;
for(int k=first[x];k;k=a[k].next){
int y=a[k].y;
if(d[y]>d[x]+a[k].c)q.push((g){d[y]=d[x]+a[k].c,y});
}
}
LL ans=0;
for(i=0;i<mn;i++)if(d[i]<=b2){
LL l=max(0ll,(b1-d[i])/mn);
if(l*mn+d[i]<b1)l++;
LL r=(b2-d[i])/mn;
ans+=r-l+1;
}
printf("%lld\n",ans);
return 0;
}
3.我真不想写背景
【题目描述】
某巨魔有一风扇,长得和下图一个怂样。这风扇原来有 N 个等分叶片,顺时针编号 1到 N(谁是编号 1 这不重要)。任意两个相邻的叶片间隔相等,大小质地相同。这个 N 满足 N=Pa∗Qb ,P,Q 为质数,a,b 为自然数。风扇一开始还不错,但是到后来风扇开始报复社会了。它的叶片从窗户甩了出去甩到了大街上,砸肿了二胖(二胖以前身材很好,你懂得)。风扇的叶片脱落是很危险的,因为这会使风扇失去平衡。可能当你经过风扇下方时它就直接拿你一血。但是左图风扇是平衡的,因为他的重心在风扇的正中心(至于为什么自己想吧)。现在给你一烂风扇。需要你判断它平不平衡,如果不平衡,那么判断它最少要拆掉它的多少叶片才能平衡。
【输入格式】
第一行两个数字 N,M,表示该风扇原来有 N 个叶片现在已经脱落了 M 个叶片。
接下来 M 行,每行一个数字,表示脱落的叶片编号,按升序给出。
【输出格式】
一个数字,表示为使电风扇平衡所需要拆掉的叶片的最少数量。
【样例输入输出】
damn.in
12 5
1
4
5
9
10
damn.out
0
【样例解释】
拆完后就是上图那怂电风扇。
【数据范围】
30%的数据:
1≤N≤10
。
50%的数据:
1≤N≤10000
。
100%的数据:
1≤N≤2∗106
,
M≤N
。
【题解】
然而这个题目其实要用网络流,本人并不会,还是放弃。
【代码】
#include <cstdio>
int main() {
freopen("damn.in","r",stdin);
freopen("damn.out","w",stdout);
puts("0");//10分
return 0;
}
总结
这次考试其实第二题没有往图论方面去想,导致了完全背包走人的结果,不过似乎大家都没想出来。。。