开始全线停课了……
今天考的是JOI(Japanese Olympiad in Informatics)……
数据测了一个中午,无语了。。。。。。
貌似明天是day2的提=题,(日本总决赛的题让我这种zz做真的好吗?)
每日推荐:
今天破例推荐一部番:黑之契约者1 黑之契约之2
一共有两季+1OVA,感觉虽然画风不够精美(毕竟时代久远~),但是剧情与人设很不错!(特别是黑叔(影帝)给人的感觉)
B站上有,所以就只给贴吧里的EX资源吧。
百度云:http://pan.baidu.com/s/1skJtIHJ
复制&粘贴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
思考
一开始想写链表……然而能力有限,就直接用string的函数了(狂暴MLE……)
正解应该是打标记来“dp”吧。
题解
T1复制&粘贴2题解
算法一:
直接模拟每一次的操作;
将要粘贴的那一段临时扔到一个数组里,然后将插入的位置后面的字符串右移,在将粘贴的部分复制进去就好了;
每次操作是O(len)=O(m)的,共操作n次,时间复杂度O(nm);
注意数组越界问题,可以拿到40分;
算法二:
注意到题中所给的K非常小,所以我们设状态f[i][j]表示,最后在第j位置上的元素在第i次操作之后在f[i][j]位置上;
边界为f[n][j]=j,然后倒着递推回去;
每次操作有可能是不变,从某处粘贴过来,或者是向右平移;
讨论一下转移即可,最后得到了f[0][j]输出即是答案;
时间复杂度O(nK),由于第一维可以滚动,所以空间复杂度O(m);
代码
//经我优化了后的标答
//实在看不惯有"输出优化"却没有"读入优化"
//并且还加上了什么调用数组地址……全删了,改改改!!!
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 200200
using namespace std;
int n,m,k;
char s[M];
int a[M],b[M],c[M];
int ans[220];
inline void read(int &res){
static char ar;
while ((ar = getchar()) < '0' || ar > '9');
res = ar - 48;
while ((ar = getchar()) >= '0' && ar <= '9')
res = res * 10 + ar - 48;
}
int main(){
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
read(k);read(m);
scanf("%s",s+1);
read(n);
for(int i=1;i<=n;i++){
read(a[i]);read(b[i]);read(c[i]);
}
for(int i=1;i<=k;i++)ans[i]=i;
for(int i=n;i;i--)
for(int j=1;j<=k;j++){
if(ans[j]<=c[i])continue;
else if(ans[j]<=c[i]+b[i]-a[i])
ans[j]=ans[j]-c[i]+a[i];
else
ans[j]-=b[i]-a[i];
}
for(int i=1;i<=k;i++)putchar(s[ans[i]]);
return 0;
}
time_total=3.33s
愉快的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
破口大骂
这翻译我是真的没有读懂……
最后考完在某大神的帮助下弥补了智商的不足,读懂题后就AC了。
题解
T2愉快的logo设计题解
算法一:
我们很容易发现,实际上一个合法的字符串大多数字符都是固定好了的,而只有一个字符是任意的;
那么处理出合法的字符串,然后枚举环上每一个可能开始读的位置,暴力计算不匹配的个数,取最小值为答案即可;
时间复杂度O((4n)2);
算法二:
考虑优化算法一,我们每一次做一次匹配的代价太大了;
因为题中的合法串有很长一段的连续相同字符,那么经过一次右移之后,有些地方匹配的字母是相同的,不用重复计算;
而必须要计算的是每一段’J’,’O’,’I’的首位,这些位置的个数只有n的级别,直接暴力处理就可以了;
时间复杂度O(4n+(4n)*n)=O(n*4n);
说白了:
就是用队列的思想模拟进出的过程,求最大匹配量。
code
//这份标答值得学习,简洁而……
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1050000
#define P(x) ((x)=='J'?0:(x)=='O'?1:2)
#define Sum(l,r,type) (sum[r][type]-sum[l-1][type])
using namespace std;
int k,n;
char s[M<<1];
int sum[M<<1][3],f[M<<1][11];
int main()
{
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
int i,j;
cin>>k;n=1<<k*2;
scanf("%s",s+1);
for(i=1;i<=n;i++)
s[i+n]=s[i];
for(i=1;i<=n<<1;i++)
for(j=0;j<3;j++)
sum[i][j]=sum[i-1][j]+(P(s[i])==j);
for(j=1;j<=k;j++)
for(i=1;i+(1<<j*2)-1<=n<<1;i++)
f[i][j]= Sum(i,i+(1<<(j-1)*2)-1,1)
+Sum(i,i+(1<<(j-1)*2)-1,2)
+Sum(i+(1<<(j-1)*2),i+(2<<(j-1)*2)-1,0)
+Sum(i+(1<<(j-1)*2),i+(2<<(j-1)*2)-1,2)
+Sum(i+(2<<(j-1)*2),i+(3<<(j-1)*2)-1,0)
+Sum(i+(2<<(j-1)*2),i+(3<<(j-1)*2)-1,1)
+f[i+(3<<(j-1)*2)][j-1];
int ans=0x3f3f3f3f;
for(i=1;i<=n;i++)
ans=min(ans,f[i][k]);
cout<<ans<<endl;
return 0;
}
time_total=6.55s
然后是我自己改出来的蒟蒻算法:
#include<cstdio>
#include<iostream>
#include<cstring>
#define INF 2100000000
using namespace std;
const int N=2200000;
char q[N];
int f[N][4];
int n,k,tot,len,t,temp;
int ans=INF;
int main(){
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
scanf("%d",&k);
n=1<<(2*k);
for(int i=1;i<=n;i++){
char c=getchar();
while(c!='J'&&c!='O'&&c!='I')c=getchar();
q[i]=c;q[i+n]=c;
}
len=(1<<(2*k-2));
tot=len;
for(int i=1;i<=n*2;i++){
for(int j=1;j<=3;j++)f[i][j]=f[i-1][j];
if(q[i]=='J')f[i][1]++;
if(q[i]=='O')f[i][2]++;
if(q[i]=='I')f[i][3]++;
}
for(int i=1;i<=n;i++){
len=tot;t=0;temp=i;
while(len){
for(int j=1;j<=3;j++)t+=f[temp+len*j-1][j]-f[temp+len*(j-1)-1][j];
temp+=len*3;
len>>=2;
}
ans=min(ans,n-t-1);
}
printf("%d",ans);
return 0;
}
time_total=3.24s(改了半天,终于比标程快一倍)
有趣的有趣的家庭菜园(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)
thought
直接上随机数,90个数据点一个都没过……
题解
T3 有趣的有趣的家庭菜园题解
算法一:
搜索所有的状态,也就是每个IOI 草拔or 不拔,然后累加花费与
收益,更新答案;
时间复杂度O(2n*n),可以拿到30 分;
算法二:
吐槽一下,因为测评机歪,代码也水,标答没有AC
80分的标答……
//我加了读入优化后还是没有AC……
//“LJ”标答……
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
int n;
int h[M],p[M],c[M];
long long f[M],g[M];
struct Segtree{
Segtree *ls,*rs;
long long max_val,mark;
Segtree() {}
Segtree(bool)
{
ls=rs=0x0;
max_val=mark=0;
}
void Push_Up()
{
max_val=max(ls->max_val,rs->max_val);
}
void Push_Down()
{
ls->Add(mark);
rs->Add(mark);
mark=0;
}
void Add(long long val)
{
max_val+=val;
mark+=val;
}
}*tree,mempool[M<<1],*C=mempool;
void Initialize()
{
tree=0x0;
C=mempool;
}
void Build_Tree(Segtree *&p,int x,int y)
{
int mid=x+y>>1;
p=new Segtree(true);
if(x==y) return ;
Build_Tree(p->ls,x,mid);
Build_Tree(p->rs,mid+1,y);
}
void Modify(Segtree *p,int x,int y,int l,int r,long long val)
{
int mid=x+y>>1;
if(x==l&&y==r)
{
p->Add(val);
return ;
}
if(p->mark) p->Push_Down();
if(r<=mid)
Modify(p->ls,x,mid,l,r,val);
else if(l>mid)
Modify(p->rs,mid+1,y,l,r,val);
else
Modify(p->ls,x,mid,l,mid,val) , Modify(p->rs,mid+1,y,mid+1,r,val) ;
p->Push_Up();
}
void Modify(Segtree *p,int x,int y,int pos,long long val)
{
int mid=x+y>>1;
if(x==y)
{
p->max_val=max(p->max_val,val);
return ;
}
if(p->mark) p->Push_Down();
if(pos<=mid)
Modify(p->ls,x,mid,pos,val);
else
Modify(p->rs,mid+1,y,pos,val);
p->Push_Up();
}
long long Query(Segtree *p,int x,int y,int l,int r)
{
int mid=x+y>>1;
if(x==l&y==r)
return p->max_val;
if(p->mark) p->Push_Down();
if(r<=mid)
return Query(p->ls,x,mid,l,r);
if(l>mid)
return Query(p->rs,mid+1,y,l,r);
return max(Query(p->ls,x,mid,l,mid) , Query(p->rs,mid+1,y,mid+1,r));
}
int main()
{
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
static pair<int,int*> b[M];
int i;
cin>>n;
for(i=1;i<=n;i++)
scanf("%d%d%d",&h[i],&p[i],&c[i]);
for(i=1;i<=n;i++)
b[i]=pair<int,int*>(h[i],&h[i]);
sort(b+1,b+n+1);
int t=0;
for(i=1;i<=n;i++)
{
if(i==1||b[i].first!=b[i-1].first)
++t;
*b[i].second=t;
}
Initialize();
Build_Tree(tree,1,t);
for(i=1;i<=n;i++)
{
f[i]=Query(tree,1,t,1,h[i])+p[i];
Modify(tree,1,t,1,h[i],-c[i]);
Modify(tree,1,t,h[i],f[i]);
}
Initialize();
Build_Tree(tree,1,t);
for(i=n;i;i--)
{
g[i]=Query(tree,1,t,1,h[i])+p[i];
Modify(tree,1,t,1,h[i],-c[i]);
Modify(tree,1,t,h[i],g[i]);
}
long long ans=0;
for(i=1;i<=n;i++)
ans=max(ans,f[i]+g[i]-p[i]);
cout<<ans<<endl;
return 0;
}
code
小编的AC代码,速度够快(现有代码中最快)……
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Li(x) x<<1
#define Ri(x) x<<1|1
#define ll long long
using namespace std;
const int N = 100010;
inline void readin(int &res){
static char ch;
while ((ch = getchar()) < '0' || ch > '9');
res = ch - 48;
while ((ch = getchar()) >= '0' && ch <= '9')
res = res * 10 + ch - 48;
}
inline void readll(ll &res){
static char ch;
while ((ch = getchar()) < '0' || ch > '9');
res = ch - 48;
while ((ch = getchar()) >= '0' && ch <= '9')
res = res * 10 + ch - 48;
}
ll max(ll x,ll y){return x>y?x:y;}
struct node{
ll h,p,c;
bool operator < (const node &rhs)const{
return h<rhs.h;
}
}a[N];
bool cmp(int x,int y){return a[x].h<a[y].h;}
int n,m;
ll dp_f[N],dp_s[N],pos[N];
ll sum[N<<2],num[N<<2],ans,t;
void init(){
readin(n);
for(int i=1;i<=n;i++){
readll(a[i].h);readll(a[i].p);readll(a[i].c);
pos[i]=i;
}
sort(pos+1,pos+n+1,cmp);
for(int i=1;i<=n;i++){
if(a[pos[i]].h!=t){
t=a[pos[i]].h;
a[pos[i]].h=++m;
}else a[pos[i]].h=m;
}
}
void up(int rt){
sum[rt]=sum[Li(rt)]+sum[Ri(rt)];
}
void down(int rt){
sum[Li(rt)]+=num[rt],num[Li(rt)]+=num[rt];
sum[Ri(rt)]+=num[rt],num[Ri(rt)]+=num[rt];
num[rt]=0;
}
ll query(int L,int R,int rt,int l,int r){
if(l==L&&r==R) return sum[rt];
if(num[rt])down(rt);
int mid=(l+r)>>1;
if(R<=mid)return query(L,R,Li(rt),l,mid);
if(L>mid) return query(L,R,Ri(rt),mid+1,r);
return max(query(L,mid,Li(rt),l,mid),
query(mid+1,R,Ri(rt),mid+1,r));
}
void updatapoint(int L,int R,ll add,int rt,int l,int r){
if(l==L&&r==R){
sum[rt]+=add;
num[rt]+=add;
return;
}
if(num[rt])down(rt);
int mid=(l+r)>>1;
if(R<=mid)updatapoint(L,R,add,Li(rt),l,mid);
else
if(L>mid) updatapoint(L,R,add,Ri(rt),mid+1,r);
else updatapoint(L,mid,add,Li(rt),l,mid),
updatapoint(mid+1,R,add,Ri(rt),mid+1,r);
sum[rt]=max(sum[Li(rt)],sum[Ri(rt)]);
}
void build(ll add,int rt,int l,int r,int id){
if(l==r) {sum[rt]=add;return;}
if(num[rt])down(rt);
int mid=(l+r)>>1;
if(id<=mid)build(add,Li(rt),l,mid,id);
else build(add,Ri(rt),mid+1,r,id);
sum[rt]=max(sum[Li(rt)],sum[Ri(rt)]);
//up(rt);
}
int main(){
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
init();
memset(sum,200,sizeof(sum));
memset(num,0,sizeof(num));
build(0,1,0,m,0);
for(int i=1;i<=n;i++){
dp_f[i]=a[i].p+query(0,a[i].h,1,0,m);
updatapoint(0,a[i].h,-a[i].c,1,0,m);
build(dp_f[i],1,0,m,a[i].h);
}
memset(sum,200,sizeof(sum));
memset(num,0,sizeof(num));
build(0,1,0,m,0);
for(int i=n;i>=1;i--){
dp_s[i]=a[i].p+query(0,a[i].h,1,0,m);
updatapoint(0,a[i].h,-a[i].c,1,0,m);
build(dp_s[i],1,0,m,a[i].h);
}
for(int i=1;i<=n;i++){
ans=max(ans,dp_s[i]+dp_f[i]-a[i].p);
}
cout<<ans;
return 0;
}