NOIP2014-3-15模拟赛
Posted white_hat_hacker
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2014-3-15模拟赛相关的知识,希望对你有一定的参考价值。
Problem 1 高级打字机(type.cpp/c/pas)
【题目描述】
早苗入手了最新的高级打字机。最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧。
请为这种高级打字机设计一个程序,支持如下3种操作:
1.T x:在文章末尾打下一个小写字母x。(type操作)
2.U x:撤销最后的x次修改操作。(Undo操作)
(注意Query操作并不算修改操作)
3.Q x:询问当前文章中第x个字母并输出。(Query操作)
文章一开始可以视为空串。
【输入格式】
第1行:一个整数n,表示操作数量。
以下n行,每行一个命令。保证输入的命令合法。
【输出格式】
每行输出一个字母,表示Query操作的答案。
【样例输入】
7
T a
T b
T c
Q 2
U 2
T c
Q 2
【样例输出】
b
c
【数据范围】
对于40%的数据 n<=200;
对于100%的数据 n<=100000;保证Undo操作不会撤销Undo操作。
<高级挑战>
对于200%的数据 n<=100000;Undo操作可以撤销Undo操作。
<IOI挑战>
必须使用在线算法完成该题。
Problem 2 不等数列(num.cpp/c/pas)
【题目描述】
将1到n任意排列,然后在排列的每两个数之间根据他们的大小关系插入“>”和“<”。问在所有排列中,有多少个排列恰好有k个“<”。答案对2012取模。
【输入格式】
第一行2个整数n,k。
【输出格式】
一个整数表示答案。
【样例输入】
5 2
【样例输出】
66
【数据范围】
对于30%的数据:n <= 10
对于100%的数据:k < n <= 1000,
Problem 3 经营与开发(exploit.cpp/c/pas)
【题目描述】
4X概念体系,是指在PC战略游戏中一种相当普及和成熟的系统概念,得名自4个同样以“EX”为开头的英语单词。
eXplore(探索)
eXpand(拓张与发展)
eXploit(经营与开发)
eXterminate(征服)
——维基百科
今次我们着重考虑exploit部分,并将其模型简化:
你驾驶着一台带有钻头(初始能力值w)的飞船,按既定路线依次飞过n个星球。
星球笼统的分为2类:资源型和维修型。(p为钻头当前能力值)
1.资源型:含矿物质量a[i],若选择开采,则得到a[i]*p的金钱,之后钻头损耗k%,即p=p*(1-0.01k)
2.维修型:维护费用b[i],若选择维修,则支付b[i]*p的金钱,之后钻头修复c%,即p=p*(1+0.01c)
注:维修后钻头的能力值可以超过初始值(你可以认为是翻修+升级)
请作为舰长的你仔细抉择以最大化收入。
【输入格式】
第一行4个整数n,k,c,w。
以下n行,每行2个整数type,x。
type为1则代表其为资源型星球,x为其矿物质含量a[i];
type为2则代表其为维修型星球,x为其维护费用b[i];
【输出格式】
一个实数(保留2位小数),表示最大的收入。
【样例输入】
5 50 50 10
1 10
1 20
2 10
2 20
1 30
【样例输出】
375.00
【数据范围】
对于30%的数据 n<=100
另有20%的数据 n<=1000;k=100
对于100%的数据 n<=100000; 0<=k,c,w,a[i],b[i]<=100;保证答案不超过10^9
T1:
这题看起来挺简单,实际上比较麻烦
对于50%的数据,代码很好写:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #define MAXN 100005 6 using namespace std; 7 char s[MAXN]; 8 int p; 9 int main() 10 { 11 int T; 12 scanf("%d",&T); 13 for(int i=1;i<=T;i++){ 14 char c; 15 scanf(" %c",&c); 16 if(‘T‘==c){ 17 char x; 18 scanf(" %c",&x); 19 s[++p]=x; 20 } 21 else{ 22 int x; 23 scanf("%d",&x); 24 if(‘U‘==c){ 25 p-=x; 26 } 27 else{ 28 printf("%c\n",s[x]); 29 } 30 } 31 } 32 return 0; 33 }
对于50%+的数据,就有点麻烦了,我是直接暴力转移的,80分,两个点MLE
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 #define MAXN 100005 7 using namespace std; 8 int n; 9 char p[MAXN],X[MAXN]; 10 int b[MAXN],to[MAXN]; 11 vector<char> s[MAXN]; 12 vector<char> find(int x){ 13 if(b[x]||!x){ 14 return s[x]; 15 } 16 s[x]=find(to[x]); 17 if(‘T‘==p[x]) 18 s[x].push_back(X[x]); 19 b[x]=1; 20 return s[x]; 21 } 22 int main() 23 { 24 // freopen("type06.in","r",stdin); 25 // freopen("my.out","w",stdout); 26 scanf("%d",&n); 27 scanf(" %c",&p[1]); 28 scanf(" %c",&p[1]); 29 s[1].push_back(p[1]); 30 b[1]=1; 31 for(int i=2;i<=n;i++){ 32 scanf(" %c",&p[i]); 33 if(‘T‘==p[i]){ 34 scanf(" %c",&X[i]); 35 to[i]=i-1; 36 } 37 else{ 38 int x; 39 scanf("%d",&x); 40 if(‘U‘==p[i]){ 41 to[i]=i-x-1; 42 } 43 else{ 44 vector<char> temp(find(i-1)); 45 printf("%c\n",temp[x-1]); 46 i--; 47 n--; 48 } 49 } 50 // for(int j=0;j<s[i].size();j++){ 51 // printf("%c",s[i][j]); 52 // } 53 // printf("\n"); 54 } 55 return 0; 56 }
正解是巧妙利用转移顺序的离线算法,把转移看成一棵树,走欧拉路使得复杂度降为O(n)
(PS:代码是标程代码,我的不知道怎么回事不能AC,调了一下午ToT)
1 #include<cstdio> 2 #include<vector> 3 #define pb push_back 4 #define rep(i,n) for(int i=0;i<n;i++) 5 6 using namespace std; 7 8 const int maxn=100010; 9 int n,q,x,v,sz,tot,fa[maxn],en[maxn],pre[maxn],g[maxn],ans[maxn]; 10 char ch,a[maxn],sk[maxn]; 11 12 vector <int> lk[maxn]; 13 14 void Ins(int u,int v) 15 { 16 pre[++sz]=en[u];en[u]=sz;g[sz]=v; 17 } 18 void Type() 19 { 20 scanf("%s",&ch); 21 a[tot]=ch; 22 tot++; 23 fa[tot]=tot-1; 24 25 } 26 void Undo() 27 { 28 scanf("%d",&x); 29 tot++; 30 Ins(tot-x-1,tot); 31 fa[tot]=tot-x-1; 32 } 33 void Query() 34 { 35 scanf("%d",&x); 36 lk[tot].pb(q); 37 ans[q++]=x; 38 } 39 40 void DFS() 41 { 42 tot=x=0; 43 for(x=0;;) 44 { 45 if (en[x]) 46 { 47 v=g[en[x]]; 48 en[x]=pre[en[x]]; 49 x=v;continue; 50 } 51 if (a[x]) 52 { 53 sk[++tot]=a[x]; 54 a[x]=0; 55 x++;continue; 56 } 57 rep(i,lk[x].size()) 58 { 59 v=lk[x][i]; 60 ans[v]=sk[ans[v]]; 61 } 62 if (fa[x]+1==x) tot--; 63 if (!x) return; 64 x=fa[x]; 65 } 66 } 67 int main() 68 { 69 // freopen("type.in","r",stdin); 70 // freopen("type.out","w",stdout); 71 scanf("%d\n",&n); 72 rep(i,n) 73 { 74 scanf("%s",&ch); 75 switch (ch) 76 { 77 case ‘T‘:Type();break; 78 case ‘U‘:Undo();break; 79 case ‘Q‘:Query();break; 80 } 81 } 82 DFS(); 83 rep(i,q) printf("%c\n",ans[i]); 84 return 0; 85 }
T2:
这个想想看下数据范围1000,1000,肯定是dp了
考虑把n插入n-1个数中去,分类讨论下,即可得到状态转移方程:
f[i][j]=f[i-1][j-1]*(i-j)+f[i-1][j]*(j+1)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #define MAXN 1005 6 #define MOD 2012 7 using namespace std; 8 int f[MAXN][MAXN]; 9 int n,k; 10 int main() 11 { 12 scanf("%d%d",&n,&k); 13 for(int i=1;i<=n;i++){ 14 f[i][0]=1; 15 } 16 for(int i=2;i<=n;i++){ 17 for(int j=1;j<n;j++){ 18 f[i][j]=(f[i-1][j-1]*(i-j)+f[i-1][j]*(j+1))%MOD; 19 } 20 } 21 printf("%d\n",f[n][k]); 22 return 0; 23 }
T3:
这题我想了好久啊,还是没有什么思路,泪奔~~~~~o(>_<)o ~~
暴力算法:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 #define MAXN 100005 7 using namespace std; 8 int n; 9 double k,c,w; 10 vector<pair<double,double> > s; 11 int main() 12 { 13 // freopen("data.in","r",stdin); 14 scanf("%d",&n); 15 scanf("%lf%lf%lf",&k,&c,&w); 16 k=(1-0.01*k),c=(1+0.01*c); 17 s.push_back(make_pair(0,w)); 18 double ans=0; 19 for(int i=1;i<=n;i++){ 20 int type,t; 21 scanf("%d",&type); 22 int sz=s.size(); 23 if(1==type){ 24 scanf("%d",&t); 25 for(int j=0;j<sz;j++){ 26 double m=s[j].first,p=s[j].second; 27 m+=t*p; p*=k; 28 ans=max(ans,m); 29 s.push_back(make_pair(m,p)); 30 } 31 } 32 else{ 33 scanf("%d",&t); 34 for(int j=0;j<sz;j++){ 35 double m=s[j].first,p=s[j].second; 36 m-=t*p; p*=c; 37 ans=max(ans,m); 38 s.push_back(make_pair(m,p)); 39 } 40 } 41 } 42 printf("%.2f",ans); 43 return 0; 44 }
得了10分QAQ
其实似乎可以优化下的,比如钱少性能又坏,那么肯定不要了
正解的思路妙啊~
发现开采:得到a[i]*p的金钱 之后钻头损耗k%,即p=p*(1-0.01k)
维修:支付b[i]*p的金钱,之后钻头修复c%,即p=p*(1+0.01c)
如果把最优解用一个多项式表示出来的话,肯定有一个公因数p!
那么我们就提取这个公因数,发现之后的无论咋搞,与p没关系了!
那么我们不妨认为p=1,然后万事大吉~
设f[i]表示开采i~n的最优解,且初始强度为1
那么有f[i]=max(f[i+1],f[i+1]*(1-0.01k)+a[i]或者是f[i+1]*(1+0.01c)-b[i])
ans为f[1]*w AC!
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #define MAXN 100005 6 using namespace std; 7 int n; 8 double f[MAXN]; 9 double k,c,w; 10 double a[MAXN],b[MAXN]; 11 12 int main() 13 { 14 scanf("%d",&n); 15 scanf("%lf%lf%lf",&k,&c,&w); 16 k=(1-0.01*k),c=(1+0.01*c); 17 for(int i=1;i<=n;i++){ 18 int t; 19 scanf("%d",&t); 20 scanf("%lf",&a[i]); 21 if(t==1){ 22 b[i]=k; 23 } 24 else{ 25 a[i]*=-1; 26 b[i]=c; 27 } 28 } 29 for(int i=n;i>=1;i--){ 30 f[i]=max(f[i+1],f[i+1]*b[i]+a[i]); 31 } 32 printf("%.2f",f[1]*w); 33 return 0; 34 }
以上是关于NOIP2014-3-15模拟赛的主要内容,如果未能解决你的问题,请参考以下文章