YNU.cpc

横浜国立大学競技プログラミング部 ( YNU.cpc )

活動日記 day 29

活動内容 :
コンテスト ( 個人戦 )
コンテスト : AtCoder Virtual Contest

A - 足し算
n個1を出力すれば良いです。
コード:

#include<bits/stdc++.h>
 
#define REP(i,s,n) for(int i=s;i<n;++i)
#define rep(i,n) REP(i,0,n)
 
using namespace std;

int main() {
  int n;
  cin >> n;
  cout << n << endl;
  rep(i,n) cout << 1 << endl;
  return 0;

B - 嘘つきの高橋くん
同じ町を2回以上訪れている場合は明らかに最短ではないです。
a,b,P[i]の中に現れる整数で、その出現回数が2回以上のものが存在すればNO, そうでなければ YESです。
コード:

#include<bits/stdc++.h>
 
#define REP(i,s,n) for(int i=s;i<n;++i)
#define rep(i,n) REP(i,0,n)
 
using namespace std;
 
typedef long long ll;
 
int main() {
  int n,a,b,k;
  cin >> n >> a >> b >> k;
  set<int> S;
  S.insert(a);
  S.insert(b);
  bool mini = true;
  rep(i,k) {
    int p;
    cin >> p;
    if( S.count(p) ) mini = false;
    S.insert(p);
  }
  cout << ( mini ? "YES" : "NO" ) << endl;
  return 0;
}

C - 正直者の高橋くん
辺の重みが1なので、幅優先で最短経路を計算しつつ
実際にそのような経路が何通りあるのか計算しましょう。

コード:

#include<bits/stdc++.h>
 
#define REP(i,s,n) for(int i=s;i<n;++i)
#define rep(i,n) REP(i,0,n)
 
using namespace std;
 
typedef long long ll;
 
#define MAX_V 110
 
const ll mod = 1000000007;
 
int V,s,t,E;
vector<int> G[MAX_V];
int mini[MAX_V]; // 最短ステップ
ll ans[MAX_V];  // 最短経路の総数
 
void compute() {
  rep(i,V) mini[i] = INT_MAX;
  mini[s] = 0;
  ans[s] = 1;
  deque<int> deq;
  deq.push_back(s);
  while( !deq.empty() ) {
    int cur = deq.front(); deq.pop_front();
    rep(i,(int)G[cur].size()) {
      int pv = G[cur][i];
      if( mini[cur] == mini[pv] + 1 ) {
	( ans[cur] += ans[pv] ) %= mod;
      }
    }
    if( cur == t ) break;
    rep(i,(int)G[cur].size()) {
      int nx = G[cur][i];
      if( mini[nx] > mini[cur] + 1 ) {
	mini[nx] = mini[cur] + 1;
	deq.push_back(nx);
      }
    }
  }
  cout << ans[t] << endl;
}
 
int main() {
  cin >> V;
  cin >> s >> t;
  --s, --t;
  cin >> E;
  rep(i,E) {
    int x,y;
    cin >> x >> y;
    --x, --y;
    G[x].push_back(y);
    G[y].push_back(x);
  }
  compute();
  return 0;
}

D - 多重ループ
n * (n+1) * ... * ( n+k-1 ) / ( 1 * 2 * ... * k ) が答えです。
modの割り算が必要になるので逆元を求めなければなりません。

コード (100点):

#include<bits/stdc++.h>
 
#define REP(i,s,n) for(int i=s;i<n;++i)
#define rep(i,n) REP(i,0,n)
 
using namespace std;
 
typedef long long ll;
 
#define MAX 300000
 
long long mod = 1000000007;
 
 
long long extgcd(long long a,long long b,long long& x,long long& y)
{
  long long d = a;
  if(b != 0){
    d = extgcd(b,a%b,y,x);
    y -= (a/b)*x;
  }
  else
    x = 1,y = 0;
  return d;
}
 
long long mod_inv(long long a,long long m)
{
  long long x,y;
  extgcd(a,m,x,y);
  return (m+x%m)%m;
}
 
int n,k;
long long frac[MAX], denom[MAX];
 
long long f(ll x) {
  if( x == 1 ) return 1;
  return ( ( frac[x+k-1] * mod_inv(frac[x-1],mod) ) % mod * mod_inv(frac[k],mod) ) % mod;
}
 
int main() {
  frac[0] = frac[1] = 1;
  REP(i,2,MAX) frac[i] = ( frac[i-1] * (ll)i ) % mod;
  cin >> n >> k;
  cout << f(n) << endl;
  return 0;
}

99点までは動的計画法でとれます。
O(n^2)です。
dp[i][j] = i番目の要素までで選んだ値の最大値がjであるときの総数

dp[i][j] = dp[i-1][1] + dp[i-1][2] + ... + dp[i-1][j]
で求めることができます。
dp[i][j-1] = dp[i-1][1] + dp[i-1][2] + ... + dp[i-1][j-1]
であることに注目すると
dp[i][j] = dp[i][j-1] + dp[i-1][j]
と書き直すことができます。
この式でループを回せばO(n^2)でとけます。

コード (99点):

#include<bits/stdc++.h>
 
#define REP(i,s,n) for(int i=s;i<n;++i)
#define rep(i,n) REP(i,0,n)
 
using namespace std;
 
typedef long long ll;
 
#define MAX_N 1010
#define MAX_K 1010
 
long long mod = 1000000007;
 
int n,k;
ll dp[MAX_N][MAX_K];
 
int main() {
  dp[0][1] = 1;
  cin >> n >> k;
if( !( n <= 1000 && k <= 1000 ) ) assert(false);
  REP(i,1,k+1) {
    REP(j,1,n+1) {
      dp[i][j] = ( dp[i][j-1] + dp[i-1][j] ) % mod;
    }
  }
  long long sum = 0;
  REP(j,1,n+1) {
    ( sum += dp[k][j] ) %= mod;
  }
 
  cout << sum  << endl;
  return 0;
}

C - 増築王高橋君
55点しかとれませんでした。
貪欲に選んでゆけば良いのですが、

55点解法は、
AOJにあるワカサギ釣りと同じ感じです。
priority_queueに値を入れて、今の値とpriority_queueの値が一致していればその値は正しいので選択します。
一致していないのであれば古い値なのでpopします。
コード(55点):

#include<bits/stdc++.h>
 
#define REP(i,s,n) for(int i=s;i<n;++i)
#define rep(i,n) REP(i,0,n)
 
using namespace std;
 
typedef long long ll;
 
#define MAX 100010
 
struct Data {
  int id,v;
  bool operator < ( const Data &data ) const {
    if( v != data.v ) return v > data.v;
    return id < data.id;
  }
};
 
int K,N;
ll A[MAX],D[MAX];
 
int get(priority_queue<Data> &Q) {
  while( !Q.empty() && A[Q.top().id] != Q.top().v ) {
    Q.pop();
  }
  assert( !Q.empty() );
  return Q.top().id;
}
 
void compute() {
  priority_queue<Data> Q;
  rep(i,N) Q.push((Data){i,A[i]});
  long long ans = 0;
  rep(i,K) {
    int ID = get(Q);
    ans += A[ID];
    A[ID] += D[ID];
    Q.push((Data){ID,A[ID]});
  }
  cout << ans << endl;
}
 
int main() {
  cin >> K;
  cin >> N;
  rep(i,N) cin >> A[i] >> D[i];
  compute();
  return 0;
}