NOIP模拟賽 by coolyangzc
共3道題目,時間3小時
題目名 | 進階打字機 | 不等數列 | 經營與開發 |
源檔案 | type.cpp/c/pas | num.cpp/c/pas | exploit.cpp/c/pas |
輸入檔案 | type.in | num.in | exploit.in |
輸出檔案 | type.out | num.out | exploit.out |
時間限制 | 1000MS | ||
記憶體限制 | 256MB | ||
測試點 | 5+(5) | 10 | |
測試點分值 | 20 |
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
【樣例輸出】
b
c
【資料範圍】
對于40%的資料 n<=200;
對于100%的資料 n<=100000;保證Undo操作不會撤銷Undo操作。
<進階挑戰>
對于200%的資料 n<=100000;Undo操作可以撤銷Undo操作。
<IOI挑戰>
必須使用線上算法完成該題。
/*第一題:對于百分之200的資料直接放棄了,就沖着前100分打了個模拟,非常水,聽說要拿到另外的100分,要用可持久化線段樹。*/
#define N 100010
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
int l=0,x,n;
char s[10],a[10],xl[N];
int main()
{
freopen("type.in","r",stdin);
freopen("type.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%s",s);
if(s[0]=='T')
{
scanf("%s",a);
xl[++l]=a[0];
}
else if(s[0]=='U')
{
scanf("%d",&x);
l-=x;
}
else {
scanf("%d",&x);
printf("%c\n",xl[x]);
}
}
fclose(stdin);fclose(stdout);
return 0;
}
Problem 2 不等數列(num.cpp/c/pas)
将1到n任意排列,然後在排列的每兩個數之間根據他們的大小關系插入“>”和“<”。問在所有排列中,有多少個排列恰好有k個“<”。答案對2012取模。
第一行2個整數n,k。
一個整數表示答案。
5 2
66
對于30%的資料:n <= 10
對于100%的資料:k < n <= 1000,
/*第二題是個DP,我覺得還是比較簡單的,得了滿分
這個DP方程:f[i][j]=(f[i-1][j]*(j+1)%mod+f[i-1][j-1]*(i-j)%mod)%mod;
拿幾個例子看看就可以得出了。
*/
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#define N 1100
#define mod 2012
int f[N][N]={0};
int n,k;
int main()
{
scanf("%d%d",&n,&k);
f[1][0]=1;
for(int i=2;i<=n;++i)
for(int j=0;j<=i-1&&j<=k;++j)
{
if(j>=1)
f[i][j]=(f[i-1][j]*(j+1)%mod+f[i-1][j-1]*(i-j)%mod)%mod;
else if(j==0)
{
f[i][j]=f[i-1][j]*(j+1)%mod;
}
}
printf("%d\n",f[n][k]%mod);
fclose(stdin);fclose(stdout);
return 0;
}
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
/*打了個2^n暴力代碼,本來能得10分,結果手賤想打個剪枝,打錯了,嗨爆了空間(以後一定注意double是8個位元組)*/
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 100010
struct XQ{
int flag;
double fle;
}stra[N];
int n;
double k,c,w,ans=0;
void input()
{
scanf("%d%lf%lf%lf",&n,&k,&c,&w);
for(int i=1;i<=n;++i)
scanf("%d%lf",&stra[i].flag,&stra[i].fle);
}
void dfs(int u,double p,double mone)
{
if(u==n+1)
{
ans=max(ans,mone);
return;
}
if(p<=0&&stra[u].flag!=2) return;
if(stra[u].flag==1)
dfs(u+1,p*(1-0.01*k),mone+stra[u].fle*p);
else dfs(u+1,p*(1+0.01*c),mone-stra[u].fle*p);
dfs(u+1,p,mone);
}
int main()
{
freopen("exploit.in","r",stdin);
freopen("exploit.out","w",stdout);
input();
dfs(1,w,0);
printf("%0.2lf",ans);
fclose(stdin);fclose(stdout);
return 0;
}
正确題解:
/*題解
對于k=100的情況,貪心
對于100%的資料
可以發現,目前的決策隻對後面的開采有影響,且剩餘耐久度與之後的開采收益成正比,
如果倒着考慮這個問題,得出i-n的星球1點耐久度所能獲得的最大收益,
從後往前dp,得出最大值最後乘w就是答案*/
/*這道題目隻要能想出倒着處理,一切都迎刃而解了!!*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 100010
int flag[N];
double a[N];
int n;
double k,c,w;
double ans=0;
void input()
{
scanf("%d%lf%lf%lf",&n,&k,&c,&w);
for(int i=1;i<=n;++i)
{
scanf("%d%lf",&flag[i],&a[i]);
}
k=(1-0.01*k);
c=(1+0.01*c);
}
int main()
{
input();
for(int i=n;i>=1;--i)
{
if(flag[i]==1)
ans=max(ans,ans*k+a[i]);
else ans=max(ans,ans*c-a[i]);
/*ans就是i之後的最大收益,因為剩餘耐久度與之後的開采收益成正比,是以如果目前取了,那麼之後的結果都會有*k,或者*c的影響,是以倒序來做是非常好做的*/
}
printf("%0.2lf",ans*w);
return 0;
}