らての精進日記

修行をします

joi2018 本選

はじめに

うくにきあ ちょっと変えると やきにくや

1問目 STOVE

貪欲します
予想難易度:5

#include<bits/stdc++.h>
using namespace std;

#define int long long

#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
#define all(v) (v).begin(),(v).end()
#define fi first
#define se second
typedef vector<int>vint;
typedef pair<int,int>pint;
typedef vector<pint>vpint;

template<typename A,typename B>inline void chmin(A &a,B b){if(a>b)a=b;}
template<typename A,typename B>inline void chmax(A &a,B b){if(a<b)a=b;}

signed main(){
    int N,K;
    cin>>N>>K;
    vint T(N);
    rep(i,N)cin>>T[i];
    int ans=T.size();
    vint lis;
    for(int i=0;i+1<T.size();i++)lis.pb({T[i+1]-T[i]-1});
    sort(all(lis));

    rep(i,N-K)ans+=lis[i];
    cout<<ans<<endl;
    return 0;
}

2問目 ART

やるだけ
類題:
cf17-relay-open.contest.atcoder.jp
予想難易度:5.5

#include<bits/stdc++.h>
using namespace std;

#define int long long

#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
#define all(v) (v).begin(),(v).end()
#define fi first
#define se second
typedef vector<int>vint;
typedef pair<int,int>pint;
typedef vector<pint>vpint;

template<typename A,typename B>inline void chmin(A &a,B b){if(a>b)a=b;}
template<typename A,typename B>inline void chmax(A &a,B b){if(a<b)a=b;}

int S1[555555];
int S2[555555];

signed main(){
    int N;
    cin>>N;
    vpint A;
    rep(i,N){
        int a,b;cin>>a>>b;
        A.pb({a,b});
    }
    sort(all(A));
    for(int i=0;i<N;i++){
        S1[i+1]=S1[i]+A[i].se;
    }
    rep(i,N)S1[i+1]-=A[i].fi;

    for(int i=0;i<N;i++){
        S2[i+1]=S2[i]+A[i].se;
        S2[i]-=A[i].fi;
    }

    int ans=0;
    int mi=S2[0];
    for(int i=1;i<=N;i++){
        chmax(ans,S1[i]-mi);
        chmin(mi,S2[i]);
    }

    cout<<ans<<endl;
    return 0;
}

3問目 Dango Maker

これかなり良問だと思います。
マス目中の各Gに対して、次の3つのうち1つを割り当てます
1.横方向の団子の真ん中としてつかう
2.縦方向の団子の真ん中としてつかう
3.つかわない

これらは独立に決めてもよいでしょうか?だめです
しかし、斜め方向の各ラインごとに独立に決めていいことはわかります。

(
XXY
XGX
YXX

上のようになっていたとして、真ん中のGの割り当てを決めたときに、影響を及ぼすのはYの位置にあるGだけなのです)

よって、斜め方向の各ラインについてdpをすると、O(NM)で解ける

予想難易度:8(ちょっと過大評価しすぎ?でも考察が面白いし、個人的に9でもそこまで違和感を感じない)

#include<bits/stdc++.h>
using namespace std;

#define int long long

#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
#define all(v) (v).begin(),(v).end()
#define fi first
#define se second
typedef vector<int>vint;
typedef pair<int,int>pint;
typedef vector<pint>vpint;

template<typename A,typename B>inline void chmin(A &a,B b){if(a>b)a=b;}
template<typename A,typename B>inline void chmax(A &a,B b){if(a<b)a=b;}

int H,W;
string S[3333];

int dp[3333][2];

int solve(int y,int x){
    vint v;
    for(int k=0;y-k>=0&&x+k<W;k++){
        int i=y-k;
        int j=x+k;
        if(S[i][j]!='G'){
            v.pb(0);
            continue;
        }
        int a=0;
        if(j>0&&j+1<W&&S[i][j-1]=='R'&&S[i][j+1]=='W')a++;
        if(i>0&&i+1<H&&S[i-1][j]=='R'&&S[i+1][j]=='W')a+=2;
        v.pb(a);
    }
    memset(dp,0,sizeof(dp));
    for(int i=0;i<v.size();i++){
        rep(j,2){
            rep(k,2){
                int tmp=dp[i][j];
                if((v[i]>>k&1)&&j==k)tmp++;
                chmax(dp[i+1][k],tmp);
            }
        }
    }
    return max(dp[v.size()][0],dp[v.size()][1]);
}

signed main(){
    cin>>H>>W;
    rep(i,H)cin>>S[i];

    int ans=0;
    for(int i=0;i<H;i++)ans+=solve(i,0);
    for(int i=1;i<W;i++)ans+=solve(H-1,i);

    cout<<ans<<endl;
    return 0;
}

4問目 COMMUTER_PASS

これ3問目と位置が逆では

適切に最短S-Tパスを選んで、「Uから、パス上の頂点のどれか までの最短コスト」+「Vから、(略」を最小化したい。
まず、S,T,U,Vからdijkstraする。
次に、S-Tパスのうち、「Uから到達すべき場所」が「Vから到達すべき場所」よりS側にある場合について、dpする。(頂点を、Sからの最短距離によって順序づけると、最短経路DAG(元のグラフから、最短S-Tパスに使われる可能性がある辺のみを残したグラフ)みたいなやつに沿ってdpできる)
順序が逆の場合も同じようにできて、おわり

予想難易度:7

#include<bits/stdc++.h>
using namespace std;

#define int long long

#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
#define all(v) (v).begin(),(v).end()
#define fi first
#define se second
typedef vector<int>vint;
typedef pair<int,int>pint;
typedef vector<pint>vpint;

template<typename A,typename B>inline void chmin(A &a,B b){if(a>b)a=b;}
template<typename A,typename B>inline void chmax(A &a,B b){if(a<b)a=b;}

const int INF=1001001001001001001ll;

int N,M;
vector<pint>G[111111];

vint dijkstra(int s){
    vint dist(N,INF);
    dist[s]=0;
    priority_queue<pint,vector<pint>,greater<pint>>que;
    que.push({0,s});
    while(que.size()){
        int c,v;
        tie(c,v)=que.top();
        que.pop();
        if(dist[v]<c)continue;
        for(auto &e:G[v]){
            if(dist[e.fi]<=c+e.se)continue;
            dist[e.fi]=c+e.se;
            que.push({dist[e.fi],e.fi});
        }
    }
    return dist;
}

int dp[222222];

signed main(){
    cin>>N>>M;
    int S,T,U,V;
    cin>>S>>T>>U>>V;S--;T--;U--;V--;
    rep(i,M){
        int a,b,c;
        cin>>a>>b>>c;
        a--;b--;
        G[a].pb({b,c});
        G[b].pb({a,c});
    }

    vint distS=dijkstra(S);
    vint distT=dijkstra(T);
    vint distU=dijkstra(U);
    vint distV=dijkstra(V);

    vpint ord;
    rep(i,N)ord.pb({distS[i],i});
    sort(all(ord));

    int ans=INF;

    fill_n(dp,N,INF);
    rep(t,N){
        int v=ord[t].se;
        chmin(dp[v],distU[v]);
        chmin(ans,dp[v]+distV[v]);
        for(auto &e:G[v]){
            if(distS[v]+e.se+distT[e.fi]!=distS[T])continue;
            chmin(dp[e.fi],dp[v]);
        }
    }

    fill_n(dp,N,INF);
    rep(t,N){
        int v=ord[t].se;
        chmin(dp[v],distV[v]);
        chmin(ans,dp[v]+distU[v]);
        for(auto &e:G[v]){
            if(distS[v]+e.se+distT[e.fi]!=distS[T])continue;
            chmin(dp[e.fi],dp[v]);
        }
    }

    cout<<ans<<endl;
    return 0;
}

5問目 SNAKE_ESCAPING

なんか非想定っぽいやりかたで強引に通してしまった(半分全列挙みたいなノリで適当に)
無理かなあと思ったけど処理を出来るだけbit演算とかで簡潔にしていったら通った(最近のコンピュータすごい)

#include<bits/stdc++.h>
using namespace std;

#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
#define all(v) (v).begin(),(v).end()
#define fi first
#define se second
typedef vector<int>vint;
typedef pair<int,int>pint;
typedef vector<pint>vpint;

template<typename A,typename B>inline void chmin(A &a,B b){if(a>b)a=b;}
template<typename A,typename B>inline void chmax(A &a,B b){if(a<b)a=b;}

int X=12;

int L;
int Q;
int S[1<<20];
char buf[1<<22];

int table[1<<12];

vpint lis[81*81*81];
int ans[1111111];

void solve(int l,int r,int e,int d){
    if(d==X){
        rep(i,lis[e].size()){
            int d=lis[e][i].fi;
            int &a=ans[lis[e][i].se];
            for(int j=0;j<1<<(L-X);j++){
                /*
                bool ok=true;
                rep(k,L-X){
                    int tmp=d>>(2*k)&3;
                    if(tmp==2)continue;
                    if((j>>k&1)!=tmp)ok=false;
                }
                if(ok)a+=S[l+j];
                */
                if(!(table[j]&d))a+=S[l+j];
            }
        }
        return;
    }

    solve(l,(l+r)/2,e*3,d+1);
    solve((l+r)/2,r,e*3+1,d+1);

    rep(i,(r-l)/2)S[l+i]+=S[l+i+(r-l)/2];
    solve(l,(l+r)/2,e*3+2,d+1);
    rep(i,(r-l)/2)S[l+i]-=S[l+i+(r-l)/2];
}


signed main(){
    scanf("%d%d",&L,&Q);
    scanf("%s",buf);
    rep(i,1<<L)S[i]=buf[i]-'0';


    chmin(X,L);

    rep(i,1<<(L-X)){
        for(int j=L-X-1;j>=0;j--){
            table[i]<<=2;
            if(i>>j&1)table[i]+=2;
            else table[i]++;
        }
    }


    rep(i,Q){
        scanf("%s",buf);
        int e=0;
        rep(j,X){
            e*=3;
            if(buf[j]=='?')e+=2;
            else if(buf[j]=='1')e++;
        }
        int d=0;
        for(int j=X;j<L;j++){
            d<<=2;
            if(buf[j]=='0')d+=2;
            else if(buf[j]=='1')d++;
        }
        lis[e].pb({d,i});
    }

    solve(0,1<<L,0,0);
    rep(i,Q)printf("%d\n",ans[i]);
    return 0;
}

おわりに

初めて全完出来た(まあめちゃくちゃ易化していましたが)
予選のときも思ったけど、ボス問がJOIらしくないですね