天天看點

2016-6-19 模拟測試

 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;
}