Codeforces 121E Lucky Array
Luckyとつくけど悪問ではない。
問題をどう見てもsegment treeに見える。
「区間にk>=0を足す」「区間の定数cの数を数える」というクエリが必要で、これは「区間の最大値」と「区間の最大値の個数」を持っておけばよい。
しかしこの場合、求めたいものcが最大値でない場合があるので、最大値を超えたら適当な小さい値に変えておく。
これを行うことで、O(m log n)の操作を30個(Lucky numberの候補の数)のsegment treeの全部に行うことで解ける。
が、3*10^6に遅延評価segtreeの重いlogがつくので結構TLEが厳しい。なのでひたすら定数倍改善をする。
正解ソース(TLEするけど)は60分くらいで書きあがったんだけど、そこから定数倍改善に1.5h費やしてしまった...
せっかくなので改善前と改善後を両方載せておきます
before
#include<stdio.h> #include<vector> #include<algorithm> #include<string> #include<iostream> using namespace std; #define SIZE 131072 int data[30]={4,7,44,47,74,77,444,447,474,477,744,747,774,777,4444,4447,4474,4477,4744,4747,4774,4777,7444,7447,7474,7477,7744,7747,7774,7777}; class segtree { public: int segmax[SIZE*2]; int segnum[SIZE*2]; int flag[SIZE*2]; int gval; void init() { for(int i=SIZE;i<SIZE*2;i++) { segmax[i]=0; segnum[i]=1; flag[i]=0; } for(int i=SIZE-1;i>=1;i--) { segnum[i]=segnum[i*2]+segnum[i*2+1]; } } void update(int beg,int end,int node,int lb,int ub,int num) { if(ub<beg||end<lb) { return; } if(beg<=lb&&ub<=end) { segmax[node]+=num; flag[node]+=num; return; } if(flag[node]) { segmax[node*2]+=flag[node]; segmax[node*2+1]+=flag[node]; flag[node*2]+=flag[node]; flag[node*2+1]+=flag[node]; flag[node]=0; } update(beg,end,node*2,lb,(lb+ub)/2,num); update(beg,end,node*2+1,(lb+ub)/2+1,ub,num); if(segmax[node*2]==segmax[node*2+1]) { segnum[node]=segnum[node*2]+segnum[node*2+1]; segmax[node]=segmax[node*2]; } else { if(segmax[node*2]>segmax[node*2+1]) { segnum[node]=segnum[node*2]; segmax[node]=segmax[node*2]; } else { segnum[node]=segnum[node*2+1]; segmax[node]=segmax[node*2+1]; } } } int get(int beg,int end,int node,int lb,int ub) { if(ub<beg||end<lb) { return 0; } if(beg<=lb&&ub<=end) { if(segmax[node]==gval) { return segnum[node]; } else { return 0; } } if(flag[node]) { segmax[node*2]+=flag[node]; segmax[node*2+1]+=flag[node]; flag[node*2]+=flag[node]; flag[node*2+1]+=flag[node]; flag[node]=0; } return get(beg,end,node*2,lb,(lb+ub)/2)+get(beg,end,node*2+1,(lb+ub)/2+1,ub); } int getidx(int node,int lb,int ub) { if(lb==ub) { return lb; } if(flag[node]) { segmax[node*2]+=flag[node]; segmax[node*2+1]+=flag[node]; flag[node*2]+=flag[node]; flag[node*2+1]+=flag[node]; flag[node]=0; } if(segmax[node*2]>gval) { return getidx(node*2,lb,(lb+ub)/2); } else { return getidx(node*2+1,(lb+ub)/2+1,ub); } } }; segtree tree[30]; int main() { int num,query; scanf("%d%d",&num,&query); for(int i=0;i<30;i++) { tree[i].gval=data[i]; tree[i].init(); } for(int i=0;i<num;i++) { int zan; scanf("%d",&zan); for(int j=0;j<30;j++) { tree[j].update(i,i,1,0,SIZE-1,zan); } } for(int p=0;p<query;p++) { string str; cin>>str; if(str=="add") { int za,zb,zc; scanf("%d%d%d",&za,&zb,&zc); za--; zb--; for(int i=0;i<30;i++) { tree[i].update(za,zb,1,0,SIZE-1,zc); } } else { int za,zb; scanf("%d%d",&za,&zb); za--; zb--; int sum=0; for(int i=0;i<30;i++) { for(;;) { if(tree[i].segmax[1]<=tree[i].gval) { break; } int zan=tree[i].getidx(1,0,SIZE-1); tree[i].update(zan,zan,1,0,SIZE-1,-100000000); } sum+=tree[i].get(za,zb,1,0,SIZE-1); } printf("%d\n",sum); } } }
after
#include<stdio.h> #include<vector> #include<algorithm> #include<string> #include<iostream> using namespace std; #define SIZE 131072 int data[30]={4,7,44,47,74,77,444,447,474,477,744,747,774,777,4444,4447,4474,4477,4744,4747,4774,4777,7444,7447,7474,7477,7744,7747,7774,7777}; class segtree { public: short segmax[SIZE*2]; int segnum[SIZE*2]; short flag[SIZE*2]; int gval; inline void init(vector<int>vec) { for(int i=0;i<vec.size();i++) { segmax[i+SIZE]=vec[i]; } for(int i=SIZE;i<SIZE*2;i++) { segnum[i]=1; } for(int i=SIZE-1;i>=1;i--) { int l=i+i,r=l+1; segmax[i]=max(segmax[l],segmax[r]); segnum[i]=segnum[l]*(segmax[i]==segmax[l])+segnum[r]*(segmax[i]==segmax[r]); } } inline void update(int beg,int end,int node,int lb,int ub,int num) { if(beg<=lb&&ub<=end) { segmax[node]+=num; flag[node]+=num; return; } int l=node+node,r=l+1; if(flag[node]) { segmax[l]+=flag[node]; segmax[r]+=flag[node]; flag[l]+=flag[node]; flag[r]+=flag[node]; flag[node]=0; } if(beg<=(lb+ub)/2) { update(beg,end,l,lb,(lb+ub)/2,num); } if(end>=(lb+ub)/2+1) { update(beg,end,r,(lb+ub)/2+1,ub,num); } segmax[node]=max(segmax[l],segmax[r]); segnum[node]=segnum[l]*(segmax[node]==segmax[l])+segnum[r]*(segmax[node]==segmax[r]); } inline int get(int beg,int end,int node,int lb,int ub) { if(ub<beg||end<lb||segmax[node]!=gval) { return 0; } if(beg<=lb&&ub<=end) { return segnum[node]; } int l=node+node,r=l+1; if(flag[node]) { segmax[l]+=flag[node]; segmax[r]+=flag[node]; flag[l]+=flag[node]; flag[r]+=flag[node]; flag[node]=0; } return get(beg,end,l,lb,(lb+ub)/2)+get(beg,end,r,(lb+ub)/2+1,ub); } inline void kill(int node) { if(node>=SIZE) { segmax[node]=-20000; return; } int l=node*2,r=node*2+1; if(flag[node]) { segmax[l]+=flag[node]; segmax[r]+=flag[node]; flag[l]+=flag[node]; flag[r]+=flag[node]; flag[node]=0; } if(segmax[l]>gval) { kill(l); } if(segmax[r]>gval) { kill(r); } segmax[node]=max(segmax[l],segmax[r]); segnum[node]=segnum[l]*(segmax[node]==segmax[l])+segnum[r]*(segmax[node]==segmax[r]); } }; segtree tree[30]; int main() { int num,query; scanf("%d%d",&num,&query); vector<int>vec; for(int i=0;i<num;i++) { int zan; scanf("%d",&zan); vec.push_back(zan); } for(int i=0;i<30;i++) { tree[i].gval=data[i]; tree[i].init(vec); } for(int p=0;p<query;p++) { char tp,gav; scanf(" %c",&tp); if(tp=='a') { int za,zb,zc; scanf("%c%c%d%d%d",&gav,&gav,&za,&zb,&zc); za--; zb--; for(int i=0;i<30;i++) { tree[i].update(za,zb,1,0,SIZE-1,zc); } } else { int za,zb; scanf("%c%c%c%c%d%d",&gav,&gav,&gav,&gav,&za,&zb); za--; zb--; int sum=0; for(int i=0;i<30;i++) { if(tree[i].segmax[1]>data[i]) { tree[i].kill(1); } sum+=tree[i].get(za,zb,1,0,SIZE-1); } printf("%d\n",sum); } } }
全然原型をとどめてない...
やったことは、ifを消したりいらない値を適当に処理するのをを一気にやったりcinを消したり掛け算を減らしたり関数呼び出しの回数を減らしたりget関数の自明な枝刈りをおこなったり。最後にintをshortに変えたら爆速になって通りました。