NOIP模拟题 2016.11.14 [动态规划] [线段树优化DP] [字符串的复制粘贴DP]

复制&粘贴2(A.c/cpp/pas/in/out)
(Time Limit:1s Memory Limit:256MB)
【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 < Bi<=Li且0<=Ci<=Li (1<=i<=N)


竟然没想到是DP。。GGGG
之前做过一到类似的,是把字符替换成字符串,也是到这处理,dp[i][j]为i次操作后j对应的字符串,这道题同理,倒着处理,dp[i][j]为i此操作后下表为j的字符一开始应该在哪里。
如果dp[i+1][j]<=C[i],那么直接继承。
如果dp[i+1][j]在粘贴之后的区间内,平移到原来的位置。
如果dp[i+1][j]在粘贴之后的区间后面,那么直接减去当前的区间长度。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int INF=0x3f3f3f3f;
const int maxn = 200005;
char s[maxn];
int K,M;
struct Node
{
    int a,b,c;
    inline void read() { ::read(a);::read(b);::read(c); }
}node[maxn];
int dp[maxn];
int n;
inline void init()
{
    read(K); read(M);
    scanf("%s",s+1);
    read(n);
    for(int i=1;i<=n;i++) node[i].read();
}
void dynamic()
{
    for(int i=1;i<=K;i++) dp[i] = i;
    for(int i=n;i>=1;i--) for(int j=1;j<=K;j++)
    {
        if(dp[j] <= node[i].c) continue;
        if(dp[j] <= node[i].c + node[i].b - node[i].a) dp[j] = dp[j] - node[i].c + node[i].a;
        else dp[j] -= node[i].b - node[i].a;
    }
}
void print()
{
    for(int i=1;i<=K;i++) putchar(s[dp[i]]);
}
int main()
{
    freopen("A.in","r",stdin);
    freopen("A.out","w",stdout);
    init();
    dynamic();
    print();
    return 0;
}

愉快的logo设计(B.c/cpp/pas/in/out)
(Time Limit:1s Memory Limit:256MB)
【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


这个比较容易想到是DP,dp[k][i]表示从给出的串的第i位开始,匹配字符串Sk的最小修改数。
然后用前缀和查询前面4^(k-1) * 3长度的串,最后加上dp[k-1][i+3*4^(k-1)]。
如果害怕爆空间就滚动一维。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int INF=0x3f3f3f3f;
const int maxn = (1<<21);
int dp[2][maxn];
int sum[maxn][3];
int EXP[15];
char s[maxn];
int K,n;
inline int idx(char ch)
{
    switch(ch)
    {
        case 'J': return 0;
        case 'O': return 1;
        case 'I': return 2;
        default: return -1;
    }
}
inline void init()
{
    read(K);
    n = (1<<(K<<1));
    for(int i=1;i<=n;i++)
    {
        char ch = (char) getchar();
        while(!isalpha(ch)) ch = (char) getchar();
        s[i] = ch;
        int d = idx(ch);
        memcpy(sum[i],sum[i-1],sizeof(sum[i-1]));
        sum[i][d]++;
    }
    EXP[0] = 1;
    for(int i=1;i<=12;i++) EXP[i] = EXP[i-1]<<2;
}
inline int Sum(int L,int R,int d)
{
    if(L <= R) return sum[R][d] - sum[L-1][d];
    else return sum[n][d] - sum[L-1][d] + sum[R][d];
}
inline int TO(int x) { return x<=n? x : x-n; }
int dynamic()
{
    int cur=0,last=1;
    for(int k=1;k<=K;k++)
    {
        swap(cur,last);
        for(int i=1;i<=n;i++)
        {
            dp[cur][i] = dp[last][TO(i+3*EXP[k-1])];
            dp[cur][i] += 3*EXP[k-1];
            dp[cur][i] -= Sum(TO(i),TO(i+EXP[k-1]-1),0);
            dp[cur][i] -= Sum(TO(i+EXP[k-1]),TO(i+2*EXP[k-1]-1),1);
            dp[cur][i] -= Sum(TO(i+2*EXP[k-1]),TO(i+3*EXP[k-1]-1),2);
        }
    }
    return *min_element(dp[cur]+1,dp[cur]+n+1);
}
int main()
{
    freopen("B.in","r",stdin);
    freopen("B.out","w",stdout);
    init();
    int ans = dynamic();
    printf("%d",ans);
    return 0;
}

有趣的有趣的家庭菜园(C.c/cpp/pas/in/out)
(Time Limit:1s Memory Limit:256MB)
【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)


GGGGGG这题一看就是线段树优化DP然而写错了!!!!!GGG只能交60分暴力GGGG
错误点在于:每次单点修改的时候应该加入dp[i]的值,而不是把modify的值和original分开算!!!因为计算到dp[i]的时候已经包含了前面需要的modify信息了!!!!GGGG

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
// end template

const LL INF=(1ll<<60);
const int maxn = 100005;

// data related
struct IOI
{
    int H,P,C;
    inline void read() { ::read(H),::read(P),::read(C); }
}ioi[maxn];
int h[maxn];
int n,lim;
// end data

// segment tree related
struct Node
{
    int ch[2];
    LL MAX;
    LL add;
}node[maxn<<2];
#define ch(x,d) node[x].ch[d]
#define MAX(x) node[x].MAX
#define add(x) node[x].add
inline void update(int root) { MAX(root) = max(MAX(root<<1),MAX(root<<1|1)); }
inline void pushdown(int root)
{
    if(!add(root)) return;
    MAX(root<<1) += add(root);
    MAX(root<<1|1) += add(root);
    add(root<<1) += add(root);
    add(root<<1|1) += add(root);
    add(root) = 0;
}
void modify(int root,int l,int r,int x,int y,LL val)
{
    if(x<=l && r<=y)
    {
        add(root) += val;
        MAX(root) += val;
        return;
    }
    pushdown(root);
    int mid = (l+r)>>1;
    if(x<=mid && l<=y) modify(root<<1,l,mid,x,y,val);
    if(y>=mid+1 && r>=x) modify(root<<1|1,mid+1,r,x,y,val);
    update(root);
}
void change(int root,int l,int r,int pos,LL val)
{
    if(l == r)
    {
        /*if(orig[pos] < val)
        {
            MAX(root) += val - orig[pos];
            orig[pos] = val;
        }*/
        smax(MAX(root),val);
        return;
    }
    pushdown(root);
    int mid = (l+r)>>1;
    if(pos <= mid) change(root<<1,l,mid,pos,val);
    else change(root<<1|1,mid+1,r,pos,val);
    update(root);
}
LL query(int root,int l,int r,int x,int y)
{
    if(x<=l && r<=y) return MAX(root);
    pushdown(root);
    int mid = (l+r)>>1;
    LL ret = -INF;
    if(x<=mid && l<=y) smax(ret,query(root<<1,l,mid,x,y));
    if(y>=mid+1 && r>=x) smax(ret,query(root<<1|1,mid+1,r,x,y));
    return ret;
}
void build(int root,int l,int r)
{
    if(l == r)
    {
        MAX(root) = add(root) = 0;
        //orig[l] = 0;
        return;
    }
    int mid = (l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    update(root);
}
//end segment tree

// main
inline void init()
{
    read(n);
    for(int i=1;i<=n;i++) ioi[i].read() , h[i]=ioi[i].H;
    sort(h+1,h+n+1);
    lim = unique(h+1,h+n+1) - h - 1;
    for(int i=1;i<=n;i++) ioi[i].H = lower_bound(h+1,h+lim+1,ioi[i].H) - h;
}
bool enable[maxn];
inline LL work0()
{
    int MAX = 0;
    for(int i=1;i<=n;i++)
    {
        if(ioi[i].H >= MAX) enable[i] = true;
        smax(MAX,ioi[i].H);
    }
    MAX = 0;
    for(int i=n;i>=1;i--)
    {
        if(ioi[i].H >= MAX) enable[i] = true;
        smax(MAX,ioi[i].H);
    }
    LL ans = 0;
    for(int i=1;i<=n;i++) if(enable[i]) ans += ioi[i].P;
    return ans;
}
LL pre[maxn],suf[maxn];
LL work()
{
    memset(node,0,sizeof(node));
    build(1,1,lim);
    for(int i=1;i<=n;i++)
    {
        pre[i] = query(1,1,lim,1,ioi[i].H) + ioi[i].P;
        modify(1,1,lim,1,ioi[i].H-1,-ioi[i].C);
        change(1,1,lim,ioi[i].H,pre[i]);
    }
    memset(node,0,sizeof(node));
    build(1,1,lim);
    for(int i=n;i>=1;i--)
    {
        suf[i] = query(1,1,lim,1,ioi[i].H) + ioi[i].P;
        modify(1,1,lim,1,ioi[i].H-1,-ioi[i].C);
        change(1,1,lim,ioi[i].H,suf[i]);
    }
    LL ans = work0();
    for(int i=1;i<=n;i++) smax(ans,pre[i]+suf[i]-ioi[i].P);
    return ans;
}
int main()
{
    freopen("C.in","r",stdin);
    freopen("C.out","w",stdout);
    init();
    LL ans = work();
    printf(AUTO,ans);
    return 0;
}
原创文章 152 获赞 15 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ourfutr2330/article/details/53159254
今日推荐