square869120Contest #1(E、G)

忘れそうなのでEとGも。E以降は「アレ?この難易度でいいのか?」と思いながら解いてました。

E - 散歩 (E869120 and Path Length)

各街に自然数aiが設定されていて、街i-1と街iとの距離はa(i-1)aiで定められています。散歩する街の順番が与えられるので、街1から初めて街1で終わる散歩の距離の総和を求めてください。

解法

aiはものすごく大きいので普通に掛け算していると間に合わない。よってビットを用いた累乗で都市間の距離を求めます。

さらに、毎回街の移動をループ回しながらやるとO(N2)でTLEするので予め街1からの距離に変換しておきます。ここではlong long型ならオーバーフローしないのでmodは取りません。(取ったらWAしました)

後は街iから街jへ移動するときは(i<j)、(街1から街jの距離)ー(街1から街iの距離)で出せます。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1000000007;
ll modpow(ll a,ll b){
    ll ret=1;
    while(b){
        if(b&1)  ret=(ret*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ret;
}
int main(){
    int n,q;   cin>>n>>q;
    vector<ll> a(n),c;    c.push_back(0);
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<q;i++){
        int ic;    cin>>ic;
        c.push_back(ic-1);
    }
    c.push_back(0);
    vector<ll> dist;  dist.push_back(0);
    for(int i=1;i<n;i++){
        dist.push_back(modpow(a[i-1],a[i]));
    }
    //a0からの距離に変換
    for(int i=2;i<n;i++) dist[i]=dist[i-1]+dist[i];
    ll ans=0;
    for(int i=0;i<=q;i++){
        int a=max(c[i+1],c[i]),b=min(c[i+1],c[i]);
        ll add=dist[a]-dist[b];
        ans=(ans+add)%mod;
    }
    cout<<ans<<endl;
    return 0;
}

G - Revenge of Traveling Salesman Problem

問題概要

TSPに「スタートから累計t移動した時にその辺が消失する。それまでにその辺は渡り終わっていなければならない」というルールを追加。

最短距離とその距離の通り方が何通りあるか求める問題。

G: Revenge of Traveling Salesman Problem - square869120Contest #1 | AtCoder

解法

普通にTSP。WAを連発しました。最初は「TSP→DPの復元」、次に「TSP→TSPと同じ向きで通り数のカウント」、最後は「TSPと同時に何通りあるかカウント」と遷移していきましたが、実は全部OK。最後のが1番簡潔だけど。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
class edge{
    public:
    ll t,d,lim;
    edge(ll it,ll id,ll ilim){
        t=it;   d=id;   lim=ilim;
    }
};
vector<edge> e[20];
ll dp[16][1<<16];
ll cnt[16][1<<16];
int main(){
    ll n,m; cin>>n>>m;
    for(ll i=0;i<m;i++){
        ll s,t,d,ti;    cin>>s>>t>>d>>ti;   s--;    t--;
        e[s].push_back(edge(t,d,ti));
        e[t].push_back(edge(s,d,ti));
    }
    for(ll i=0;i<n;i++){
        for(ll j=(1<<n)-1;j>=0;j--){
            dp[i][j]=2e14; cnt[i][j]=0;
        }
    }
    dp[0][0]=0;  cnt[0][0]=1;
    for(ll i=0;i<(1<<n)-1;i++){
        for(ll j=0;j<n;j++){
            if(dp[j][i]==2e14)   continue;
            for(ll k=0;k<(ll)e[j].size();k++){
                if(dp[j][i]+e[j][k].d<=e[j][k].lim){
                    ll next=i|(1<<e[j][k].t);
                    if(next==i)   continue;
                    if(dp[e[j][k].t][next]>dp[j][i]+e[j][k].d){
                        dp[e[j][k].t][next]=dp[j][i]+e[j][k].d;
                        cnt[e[j][k].t][next]=cnt[j][i];
                    }
                    else if(dp[e[j][k].t][next]==dp[j][i]+e[j][k].d){
                        cnt[e[j][k].t][next]+=cnt[j][i];
                    }
                }
            }
        }
    }
    if(dp[0][(1<<n)-1]==2e14){
        cout<<"IMPOSSIBLE"<<endl;
        return 0;
    }
    cout<<dp[0][(1<<n)-1]<<" "<<cnt[0][(1<<n)-1]<<endl;
    return 0;
}

残りはまだ解いてません。

にしても、やはりこのコンテストは中学生主催、というのもあるのだろうけど、他のコンテストに比べて難易度が低い。

初心者向けのコンテストかな…