A-ソーシャルゲーム(Social Game)
・問題概要
JOI君はソシャゲを始める。
ログインボーナスがA(1<=A<=1000)コイン、7日連続でログインするとB(1<=B<=1000)コインもらえる。
C(1<=C<=10^6)枚のコインが欲しいとき何日ログインすべきか
・解法
・累計のコインがC以上になるまで愚直に探索
・実装
こんな感じ
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<string>
using namespace std;
typedef long long Int;
int main(){
int a,b,c;
cin>>a>>b>>c;
int cnt=0;
while(c>0){
c-=a;
cnt++;
if(cnt%7==0) c-=b;
}
cout<<cnt<<endl;
}
B-すごろくと駒(Sugoroku and Pieces)
・問題概要
・すごろくがありスタートとゴールの間に1~2019の番号が振られている
・1個先に駒がいないorゴールに着かないなら毎回指定された駒を1個右へ
・解法
・実際にそれをプログラム上でやってみる
・i番目の駒がどこにあるかの配列と、マスiに駒がいるかどうかのbool型配列を用意して実装
・実装
こんな感じ
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<string>
using namespace std;
typedef long long Int;
bool used[2020]={0};
int pos[2020]={0};
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>pos[i];
used[pos[i]]=1;
}
int m;
cin>>m;
for(int i=0;i<m;i++){
int a;
cin>>a;
if(used[pos[a]+1]==0&&pos[a]+1!=2020){
used[pos[a]]=0;
used[pos[a]+1]=1;
pos[a]+=1;
}
}
for(int i=1;i<=n;i++) cout<<pos[i]<<endl;
}
C-マルバツスタンプ
・問題概要
・JOI君は○スタンプ、×スタンプ、○×スタンプをいくつか持っている
・○×スタンプは○×と×○のどっちの押し方もできる
・JOI君がスタンプを押した残骸をもらうので、JOI君が持っている○×の個数の最大は?
・解法
・要素数分、はじめから見ていく
・i番目!=i+1番目ならそこは○×を使って押せます
・この場合i+1番目は見なくて良いのでi+2番目の探索に移ります
・実装
こんな感じ
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<string>
using namespace std;
typedef long long Int;
bool used[2020]={0};
int pos[2020]={0};
int main(){
int n;
string s;
cin>>n;
cin>>s;
int ans=0;
for(int i=0;i<n-1;i++){
if((s[i]=='O'&&s[i+1]=='X')||(s[i]=='X'&&s[i+1]=='O')){
ans++;
i++;
}
}
cout<<ans<<endl;
}
ここまで10分で埋めました。例年の過去問だと20分くらい書けてるので速いほうですね、嬉しい
D-日本沈没(Japan Sinks)
・問題概要
・日本をn個の区画に分ける。それぞれに高さA[i]がある。
・海面上昇で相対的に日本が沈む。ばいばい。
・だんだん高さが低い部分から海底になっていくが、そのとき連続していない一続きの陸地がc個あるとき島の数をcとする
・日本が完全消滅するまでに島が一番たくさんできるのはいつ?
・解法
・何も考えずに部分点1を見る。愚直にシュミレーションして間に合う。
・具体的には、日本の最高点に波が来るまで波の高さを1mずつ試す。
・毎回全体を見渡して島の数を数えていけばO(NAmax)<=4000000回の計算なので間に合う
・部分点2は高さの制約がないも同然
・そこで座標圧縮という技を使う
・具体例をみた方がわかりやすいかも ・写真の場合、波の高さが2だろうが57だろうが99だろうが、区画1と3は浮いてて区画2は沈んでる
・→0、1、100、5000兆だけ見ればいい こうすれば5000兆回試さなくて済む
・全部の高さをvectorに入れてソートする、またはpriority_queueを使うなどしてこれを実装すれば7+8=15点
・5,6の全探索を埋めて、満点解法が生えた
・この場合、5が沈むので島の数は増える
・しかし実際は1~3や7は5が沈んでも関係ない→その隣の状態だけを考えればいい
・海底:0,大陸の左端:1,大陸の右端:2,孤島:3,大陸内部:4とし、その状態がどのように変化するかを考えればO(N^2)くらいでできた
・これをACするのに2時間かけたが、これをACできたのは大きい
・実装
満点解法だけ載せます。割とごちゃごちゃしているのでわかりにくいかも
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<string>
using namespace std;
typedef unsigned long long Int;
typedef pair<Int,Int> P;
Int a[100005]={0};
vector<Int> v;
vector<P> island;
int main(){
Int n,maxa=0,zcnt=0;
cin>>n;
for(Int i=0;i<n;i++){
cin>>a[i];
maxa=max(maxa,a[i]);
if(a[i]==0) zcnt++;
v.push_back(a[i]);
island.push_back(P(a[i],i));
}
v.push_back(0);
sort(v.begin(),v.end());
sort(island.begin(),island.end());
Int p=zcnt;
Int ans=0;
int info[200005]={0};
Int is=0;
for(Int j=0;j<n;j++){
if(a[j]<=0&&is>0){
ans++;
is=0;
if(j>=1){
if(info[j-1]==1) info[j-1]=3;
else info[j-1]=2;
}
}
else {
if(is==0&&a[j]<=0) info[j]=0;
else if(is==0) info[j]=1;
else info[j]=4;
is=a[j];
}
}
if(a[n-1]<=0) info[n-1]=0;
else if(a[n-2]<0&&a[n-1]>0) info[n-1]=3;
else info[n-1]=2;
if(is>0) ans++;
Int ans2=ans;
for(Int i=0;i<v.size();i++){
Int np=0;
np=(lower_bound(island.begin(),island.end(),P(v[i]+(Int)1,(Int)0))-island.begin());
for(int j=p;j<np;j++){
Int pos=island[j].second;
if(info[pos]==4){
ans++;
if(info[pos-1]==1) info[pos-1]=3;
else info[pos-1]=2;
if(info[pos+1]==2) info[pos+1]=3;
else info[pos+1]=1;
}
else if(info[pos]==2){
if(info[pos-1]==1) info[pos-1]=3;
else info[pos-1]=2;
}
else if(info[pos]==3){
ans--;
info[pos]=0;
}
else if(info[pos]==1){
if(info[pos+1]==2) info[pos+1]=3;
else info[pos+1]=1;
}
info[pos]=0;
}
if(ans2<ans) ans2=ans;
p=np;
}
cout<<ans2<<endl;
}
E-イルミネーション(Illumination)
・問題概要
・JOI氏は木をN本はやしている。電飾してEn-JOI-ableな冬を過ごしたい。
・まぶしすぎるのは嫌なのでm個の区間[l,r]では1本の木にしか電飾しない
・木ごとに美しさがあるのでそれを最大化したい
・解説(になっていない)
・結論から言うと部分点1しかとれませんでした
・全探索をします
・具体的には・・・すべての場所で電飾のあるなしを試します。計算量はO(2^N)
・10分でこれを書いた以外何も分かりませんでした。終わった後にDPするらしいと言われ納得したけど実装ができない
・実装
bit全探索の書き方が知りたい人向けに一応・・・
ちなみにi>>jはiのビットをjだけシフトさせたと言うことです。例えば57>>2は57=111001(2)より、2つ右にずらすと001110になります。
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<string>
using namespace std;
typedef long long Int;
typedef pair<Int,Int> P;
Int a[200005]={0};
vector<P> v;
int main(){
Int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<m;i++){
Int l,r;
cin>>l>>r;
v.push_back(P(l,r));
}
Int ans=0;
for(Int i=1;i<(1<<(n+1));i++){
bool f2=true;
for(Int j=0;j<m;j++){
Int l=v[j].first,r=v[j].second;
bool f=false;
for(int k=l;k<=r;k++){
if(((i>>k)&1)==1){
if(!f) f=true;
else{
f2=false;
break;
}
}
}
}
Int kans=0;
if(f2){
for(int j=1;j<=n;j++) if(((i>>j)&1)==1) kans+=a[j];
ans=max(kans,ans);
}
}
cout<<ans<<endl;
}
F-座席(Seats)
・問題概要
>>
・国がN個あり、i番目とi+1番目は隣国
・IOIに各国からAi人が出る
・1列に並べるが、不正対策のため隣国や同国の選手を隣にしないように並べたい
・パターン数のmod10007を求めよ