noip模拟题11.14 距noip2016还剩四天

T1 复制&粘2

【Description】

文本编辑器的一个最重要的机能就是复制&粘贴。JOI社现在正在开发一款能够非常高速地进行复制&粘贴的文本编辑器,作为JOI社一名优秀的程序猿,你担负起了复制&粘贴功能的测试这一核心工作。整个JOI社的命运都系在你的身上,因此你无论如何都想写出一个正确且高速的程序来完成这项工作。
具体的做法如下所示。文件的内容是一个字符串S,对其进行N次复制&粘贴的操作,第i次操作复制位置Ai和位置Bi之间的所有文字,然后在位置Ci粘贴。这里位置x表示字符串的第x个字符的后面那个位置(位置0表示字符串的开头),例如字符串”copypaste”的位置6表示字符’a’和字符’s’之间的位置,位置9表示’e’后面的位置(即字符串的结尾)。不过,如果操作后的字符串长度超过了M,那么将超过的部分删除,只保留长度为M的前缀。
你的任务是写一个程序,输出N次操作后字符串的前K个字符。

【Input】

第一行两个空格分隔的正整数K和M,表示最终输出的文字数量和字符串长度的上限。
第二行一个字符串S,表示初始的字符串。
第三行一个整数N,表示操作的次数。
接下来N行,第i行包含三个空格分隔的整数Ai,Bi,Ci,表示第i次操作将位置Ai到Bi之间的字符串复制,然后粘贴到位置Ci。

【Output】

输出一行一个长度为K的字符串,表示最终的字符串的长度为K的前缀。

【Sample Input】

2 18
copypaste
4
3 6 8
1 5 2
4 12 1
17 18 0

【Sample Output】

ac

【HINT】

初始的字符串为”copypaste”。
第一次操作将从位置3到位置6的字符串”ypa”复制,插入位置8,得到字符串”copypastypae”
第二次操作将从位置1到位置5的字符串”opyp”复制,插入位置2,得到字符串”coopyppypastypae”
第三次操作将从位置4到位置12的字符串”yppypast”复制,插入位置1,得到字符串”cyppypastoopyppypastypae”,由于长度超过了M=18,删除超过的部分,得到”cyppypastoopyppypa”
第四次操作将从位置17到位置18的字符串”a”复制,插入位置0,得到字符串”acyppypastoopyppypa”,删除超过18的部分得到”acyppypastoopyppyp”
最后输出长度为2的前缀”ac”即为答案。

【Data Constraint】

对于40%的数据,N,M<=2000
对于100%的数据:
1<=K<=200
1<=M<=10^9
S的每个字符都是小写字母(‘a’~’z’)
K<=|S|<=min(M,2*10^5)
1<=N<=2*10^5
设第i次操作前的字符串长度为Li,那么0<=Ai

#include<ctime>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char c[200005];
int l[200005],r[200005],pos[200005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline char query(int k,int tmp)
{
    if(tmp==0)return c[k];
    else if(pos[tmp]>=k)
        return query(k,tmp-1);
    else if(k>pos[tmp]&&k<=r[tmp]-l[tmp]+pos[tmp]+1)
        return query(k-pos[tmp]+l[tmp]-1,tmp-1);
    else
        return query(k-(r[tmp]-l[tmp]+1),tmp-1);
}//插入在pos后面
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int k=read(),m=read();
    scanf("%s",c+1);
    int q;
    scanf("%d",&q);
    for(int i=1;i<=q;++i)
    {
        l[i]=read();r[i]=read();pos[i]=read();
        l[i]++;
    }
    for(int i=1;i<=k;++i)
        printf("%c",query(i,q));
    return 0;
}

愉快的logo设计(B.c/cpp/pas/in/out)

【Description】

K理事长正在思考日本信息学奥林匹克竞赛选手的应援道具的logo问题。某天,K理事长突发奇想,想要设计一个用’J’,’O’,’I’三种文字环形排列的logo,意为希望选手能从JOI中收获快乐的意思。
(注:“环形地”在日文中的表述为“円状に”,“円”读作“en”,再加上“JOI”三个字即为“enjoy”„„)
如下所示,对于任意非负整数k,我们定义标号为k的JOI序列Sk为:
·S0为’J’,’O’,’I’中任一字符构成的长度为1的字符串
·S[k+1]为最初4^k个字符都是’J’,接下来的4^k个字符都是’O’,接下来的4^k个字符都是’I’,最后4^k个字符是字符串Sk的长为4^(k+1)的字符串
现在,K理事长在纸上写下了由4^K个文字构成的一个环形字符串,字符串中每个字符都是’J’,’O’,’I’中的一个。K理事长想要修改一些文字,使得得到的字符串从某个起点开始顺时针读一圈后可以得到SK。在满足条件的情况下,要求修改的文字数量最少。

【Input】

第一行一个正整数K,表示K理事长在纸上写下了一个长度为4^K的环状字符串。
第二行一个由’J’,’O’,’I’三个字符构成的长为4^K的字符串,表示纸上的环形字符串从某个起点出发顺时针阅读一圈得到的字符串。

【Output】

输出一行一个整数,表示修改文字数量的最小值。

【Sample Input】

2
JJOIJJOJOIOJOOOI

【Sample Output】

7

【HINT】

这里写图片描述
从○标记的位置顺时针阅读一圈得到“JJJJOOOOIIIIJOIJ”,满足S2的条件,且修改文字数达到最小值7。

【Data Constraint】

对于30%的数据,1<=K<=5
对于100%的数据,1<=K<=10

考虑这道题就是从原串的不同起点开始来与目标串匹配,而每次都重新匹配肯定要T,所以用3个数组记录当前位置及以前一共有多少个J、O、I,然后每次算即可。
代码:

#include<ctime>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int ans,sumj[2097155],sumo[2097155];
int sumi[2097155],n=1;
int duan[10];
char que[2097155];
inline void init()
{
    for(int i=1;i<=n;i++)
    {
        sumj[i]=que[i]=='J'?sumj[i-1]+1:sumj[i-1];
        sumo[i]=que[i]=='O'?sumo[i-1]+1:sumo[i-1];
        sumi[i]=que[i]=='I'?sumi[i-1]+1:sumi[i-1];
    }
    for(int i=1+n;i<=2*n;i++)
    {
        sumj[i]=que[i]=='J'?sumj[i-1]+1:sumj[i-1];
        sumo[i]=que[i]=='O'?sumo[i-1]+1:sumo[i-1];
        sumi[i]=que[i]=='I'?sumi[i-1]+1:sumi[i-1];
    }
}
inline void coun(int pos,int tmp)
{
    ans+=duan[tmp]-sumj[pos+duan[tmp]-1]+sumj[pos-1];
    ans+=duan[tmp]-sumo[pos+2*duan[tmp]-1]+sumo[duan[tmp]+pos-1];
    ans+=duan[tmp]-sumi[pos+3*duan[tmp]-1]+sumi[2*duan[tmp]+pos-1];
    if(tmp==0)return ;
    coun(pos+3*duan[tmp],tmp-1);
}
int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    int k,minx=0x7f7f7f7f;
    scanf("%d",&k);
    for(int i=1;i<=k;i++)
        n*=4;
    duan[0]=1;
    for(int i=1;i<=k;i++)
        duan[i]=duan[i-1]*4;
    for(int i=1;i<=n;i++)
    {
        scanf("%c",&que[i]);
        while(que[i]=='\n')
            scanf("%c",&que[i]);
        que[i+n]=que[i];
    }
    init();
    for(int i=1;i<=n;i++)
    {
        ans=0;
        coun(i,k-1);
        minx=min(minx,ans);
    }
    printf("%d",minx);
    return 0;
}

T3 有趣的有趣的家庭菜园

【Description】

职业经营家庭菜园的JOI君每年在自家的田地中种植一种叫做IOI草的植物。IOI草的种子在冬天被播下,春天会发芽并生长至一个固定的高度。到了秋天,一些IOI草会结出美丽的果实,并被收获,其他的IOI草则会在冬天枯萎。
JOI君的田地沿东西方向被划分为N个区域,从西侧开始的第i个区域中种植着IOI草i。在第i个区域种植的IOI草,在春天的时候高度会生长至Hi,此后便不再生长。如果IOI草i会结出果实,那么将会获得Pi的收益,否则没有收益。
春天到了,查看田地样子的JOI君决定拔掉一些种植的IOI草,使利益最大化。拔掉IOI草i需要Ci的花销,拔掉的IOI草会立刻枯萎。IOI草只能在春天被拔掉,夏天和秋天不能拔掉IOI草。
IOI草是一种非常依靠阳光的植物,如果在夏天某个区域的IOI草的东侧和西侧都有比它高的IOI草存在,那么这株IOI草在秋天便不会结出果实。换句话说,为了让没有被拔掉的IOI草i在秋天结出果实,到了夏天的时候,以下两个条件至少满足一个:
1.对于任意1<=j<=i-1,Hj<=Hi或IOI草j已经被拔除
2.对于任意i+1<=j<=N,Hj<=Hi或IOI草j已经被拔除
用最终收获的果实的总价格减掉拔除IOI草的花销的总和,即为JOI君的收益。那么JOI君能从IOI草中获取的最大利益到底有多少呢?

【Input】

第一行一个正整数N,表示田地被分为了N个区域。
接下来N行,第i行(1<=i<=N)三个空白分割的正整数Hi,Pi,Ci,表示第i株IOI草在春天时高度会生长至Hi,秋天收获的果实的价格为Pi,拔除所需费用为Ci。

【Output】

输出一行一个整数,表示JOI君能获得的最大利益

【Sample Input】

7
22 60 30
46 40 30
36 100 50
11 140 120
38 120 20
24 90 60
53 50 20

【Sample Output】

320

【HINT】

拔除IOI草2和IOI草7,剩余的IOI草如下图所示:
这里写图片描述
IOI草1、3、5、6的果实价格分别为60、100、120、90,拔除IOI草2和IOI草7的花销分别为30、20,总收益为320,这是所有方案中的最大值。

【Data Constraint】

对于30%的数据,N<=20
对于45%的数据,N<=300
对于60%的数据,N<=5000
对于100%的数据:
3<=N<=10^5
1<=Hi<=10^9 (1<=i<=N)
1<=Pi<=10^9 (1<=i<=N)
1<=Ci<=10^9 (1<=i<=N)

正解要用线段树动态维护,不会…
考试时60%的dp还写错了…
至少思路是对的,所以改时改得很快。
两个数组,分别表示从前面到这个位置的最大值和从后面到这个位置的最大值,那么这个位置的草一定是要结果的草中最高的。记一个二维数组 sum[i][j] 表示 i 到 j 中拔掉比 i 草高的草的花费,i 可以比 j 大,所以状态转移方程:pre[i]=max( pre[j]-sum[j][i-1]// i 前面的草是 j +p[i]、nex[i]=max( nex[j]-sum[j][i+1]+p[i]。
代码:

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
int h[5005],c[5005],p[5005],n;
long long dp[5005],f[5005],sum[5005][5005];
void init()
{
    for(int i=0;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            sum[i][j]=sum[i][j-1];
            if(h[j]>h[i])sum[i][j]+=c[j];
        }
    for(int i=n+1;i>=1;i--)
        for(int j=i-1;j>=1;j--)
        {
            sum[i][j]=sum[i][j+1];
            if(h[j]>h[i])sum[i][j]+=c[j];
        }
    for(int i=1;i<=n;i++)dp[i]=p[i]-sum[0][i-1];
    for(int i=n;i>=1;i--)f[i]=p[i]-sum[n+1][i+1];
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&h[i],&p[i],&c[i]);
    if(n>5000)//随机数骗分,貌似没骗到
    {
        srand((int)time(0));int t=1+rand()%99999999;
        printf("%d",t);return 0;
    }
    init();dp[1]=p[1];f[n]=p[n];
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            if(h[i]>=h[j])
                dp[i]=max(dp[i],dp[j]-sum[j][i-1]+p[i]);
    for(int i=n;i>=1;i--)
        for(int j=n;j>i;j--)
            if(h[i]>=h[j])
                f[i]=max(f[i],f[j]-sum[j][i+1]+p[i]);
    long long ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,dp[i]+f[i]-p[i]);
    printf(AUTO,ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值