复制&粘贴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
愉快的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
有趣的有趣的家庭菜园(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)
第一题,遍历每个k前的,然后递推求的它的最早位置,然后最后按顺序输出。因为k比较小,又不需要全解,因此这样做。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 205000
char ss[maxn*2],t[maxn];
int k,m,a[maxn],b[maxn],c[maxn],n;
int main()
{
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
scanf("%d%d",&k,&m);
scanf("%s",ss+1);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&a[i],&b[i],&c[i]);
for(int i=1;i<=k;i++)
{
int now=i;
for(int j=n;j>=1;j--)
{
if(now<=c[j]) continue;
else if(now>c[j]+b[j]-a[j])now=(now-b[j]+a[j]);
else now=now-c[j]+a[j];
}
t[i]=ss[now];
}
for(int i=1;i<=k;i++)
putchar(t[i]);
return 0;
}
第二题,理论上可以用倍增,比如我,但是被卡空间和时间,无法全过,听说前缀和可以全过,不过理论时间复杂度都一样。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
using namespace std;
#define maxn 1050005
const int inf=maxn;
char ss[maxn];
int f[maxn][10][3],nx[maxn][10];
int len1,k,minl=inf;
int main()
{
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
scanf("%d",&k);
scanf("%s",ss+1);
int len1=strlen(ss+1);
int a;
for(int i=1;i<=len1;i++)
{
if(ss[i]=='J') a=0;
else if(ss[i]=='O') a=1;
else if(ss[i]=='I') a=2;
f[i][0][a]=1;
nx[i][0]=i-1;
}
nx[1][0]=len1;
for(int i=1;i<=k-1;i++)
for(int j=1;j<=len1;j++)
{
int tmp1=nx[j][i-1];
int tmp2=nx[tmp1][i-1];
int tmp3=nx[tmp2][i-1];
nx[j][i]=nx[tmp3][i-1];
f[j][i][0]=f[tmp1][i-1][0]+f[tmp2][i-1][0]+f[tmp3][i-1][0]+f[j][i-1][0];
f[j][i][1]=f[tmp1][i-1][1]+f[tmp2][i-1][1]+f[tmp3][i-1][1]+f[j][i-1][1];
f[j][i][2]=f[tmp1][i-1][2]+f[tmp2][i-1][2]+f[tmp3][i-1][2]+f[j][i-1][2];
}
for(int i=1;i<=len1;i++)
{
int tmp=i,tot=0,ti=k-1;
while(ti>=0)
{
tot+=(1<<(2*ti))-f[tmp][ti][0];
tmp=nx[tmp][ti];
tot+=(1<<(2*ti))-f[tmp][ti][1];
tmp=nx[tmp][ti];
tot+=(1<<(2*ti))-f[tmp][ti][2];
tmp=nx[tmp][ti];
ti--;
}
minl=min(tot,minl);
}
printf("%d\n",minl);
return 0;
}
第三题,dp+线段树+离散化,先离散化高度,枚举最高点,如果有比最高点低的f[j],则区间修改小于等于h,使其减去除该草的价值(因为保证其最高)查询最大f[j],加上p[i],即为f[i],再将f[i],插入线段树。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define maxn 100005
#define L(u) (u<<1)
#define R(u) ((u<<1)|1)
typedef long long ll;
int n;
struct num1{
ll h;
int nu;
bool operator < (const num1 &a1)const {
return h<a1.h;
}
}ini[maxn];
struct tre{
int l,r;
}nod[maxn*4];
ll sum[maxn*4],ad[maxn*4],p[maxn],c[maxn],maxl,f[maxn],g[maxn];
int num,h[maxn];
void initial() { memset(ad,0,sizeof(ad)); memset(sum,0,sizeof(sum)); }
void build(int u,int le,int ri)
{
nod[u].l=le;
nod[u].r=ri;
if(le==ri)
return ;
int mid=(le+ri)/2;
build(L(u),le,mid);
build(R(u),mid+1,ri);
}
void pushdown(int u)
{
sum[L(u)]+=ad[u];
sum[R(u)]+=ad[u];
ad[L(u)]+=ad[u];
ad[R(u)]+=ad[u];
ad[u]=0;
}
void pushup(int u) { sum[u]=max(sum[L(u)],sum[R(u)]); }
void update(int u,int le,int ri,ll val)
{
if(nod[u].l>=le&&nod[u].r<=ri)
{
sum[u]+=val;
ad[u]+=val;
return ;
}
if(ad[u]) pushdown(u);
int mid=(nod[u].l+nod[u].r)/2;
if(ri<=mid)update(L(u),le,ri,val);
else if(le>mid)update(R(u),le,ri,val);
else
{
update(L(u),le,mid,val);
update(R(u),mid+1,ri,val);
}
}
void update(int u,int ai,ll val)
{
if(nod[u].l==nod[u].r)
{
sum[u]=max(val,sum[u]);
return ;
}
if(ad[u]) pushdown(u);
int mid=(nod[u].l+nod[u].r)/2;
if(mid>=ai) update(L(u),ai,val);
else update(R(u),ai,val);
pushup(u);
}
ll query(int u,int le,int ri)
{
if(nod[u].l>=le&&nod[u].r<=ri)
{
return sum[u];
}
if(ad[u]) pushdown(u);
int mid=(nod[u].l+nod[u].r)/2;
if(ri<=mid) return query(L(u),le,ri);
else if(le>mid) return query(R(u),le,ri);
else
return max(query(L(u),le,mid),query(R(u),mid+1,ri));
}
int main()
{
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf(AUTO AUTO AUTO,&ini[i].h,&p[i],&c[i]);
ini[i].nu=i;
}
sort(ini+1,ini+n+1);
for(int i=1;i<=n;i++)
{
if(ini[i].h==ini[i-1].h)
h[ini[i].nu]=h[ini[i-1].nu];
else
h[ini[i].nu]=++num;
}
build(1,1,num);
for(int i=1;i<=n;i++)
{
f[i]=query(1,1,h[i])+p[i];
update(1,1,h[i],-c[i]);
update(1,h[i],f[i]);
}
initial();
for(int i=n;i>=1;i--)
{
g[i]=query(1,1,h[i])+p[i];
update(1,1,h[i],-c[i]);
update(1,h[i],g[i]);
}
for(int i=1;i<=n;i++)
maxl=max(f[i]+g[i]-p[i],maxl);
printf(AUTO"\n",maxl);
return 0;
}