D- People on a Line
コンテスト中に解けなくて悲しい思いをした問題。
コンテスト中に考えてたこと
・全点対最短路を求めることができたら良さそう。
・有効辺のみなので、dfs(コンテスト中はDijkstraをした)時にusedとdを持つと解ける。
(つまりdfsごとに到達と距離の配列を持つ必要がある)
・グラフは非連結の場合がある。↑の方針だとグラフの数だけ配列が必要になって解けそうにない。
正解のときに考えたこと
・1つの座標を決めることで、全ての座標が決まる。
・逆辺を貼ることで、一度のdfsで到達したか、どうかを判定する配列を持つ必要がない。
・これトポロジカル順にソートすると逆辺はらなくてよくない?
・訪問した頂点の全ての辺について探索を行うので、再訪問の必要はない(よって間に合う。
トポロジカルソートでもうまいことやると解けるらしい。
#include<bits/stdc++.h> using ll = long long; #define int ll #define INF 100000000000000LL #define rep(i,n) for(int i=0;i<n;i++) using namespace std; using pii = pair<int,int>; vector<bool>used; vector<vector<pii>>edge; vector<int>d; bool judge=true; void dfs(int v,int val){ used[v]=true; rep(i,edge[v].size()){ int next=edge[v][i].first; int cost=edge[v][i].second; if(d[next]==INF){ d[next]=val+cost; if(!used[next])dfs(next,d[next]); }else if(d[next]!=cost+val){ judge=false; } } } signed main(){ int n,m; cin>>n>>m; edge = vector<vector<pii>>(n); used = vector<bool>(n,false); d = vector<int>(n,INF); rep(i,m){ int a,b,c;cin>>a>>b>>c; --a,--b; edge[a].push_back(make_pair(b,c)); edge[b].push_back(make_pair(a,-c)); } rep(i,n)if(!used[i])dfs(i,0); cout << (judge ? "Yes" : "No") << endl; }
2019/6月追記
コードがひどすぎたので直した。
RiPProを休部する話
RiPProを休部することにしました。
※この記事は完全にポエムです。
理由をメンヘラにならないように簡潔に書く。
①留学の準備のために1日3,4時間ほど勉強する必要がでてきた。
留学のために2017年の前半を溶かしたといっても過言ではないので、留学はしっかり結果を残したい。そのためにも、準備をきちんとすることにした。
②競プロが辛い。
*1個人的に競プロの世界は弱肉強食だと思っているので、レートが上がらない、強くなれてる気がしないという状況に、そろそろ辛くなって来た。
精進が足りないと言われれば、それまでなんだけど、*2考えが変わるまで自分のペースでゆったり競プロをやることにした。
来年の4月までいません。
迷惑をかけるかもしれませんが、オネガイシマス。
D-塗り絵
D: 塗り絵 - AtCoder Beginner Contest 036 | AtCoder
個の島があり、個の橋がある。
島を白または黒に塗っていく。ただし、両端の島を黒で塗ることはできない。
色の塗り方が何通りあるか求める問題です。
グラフ上の頂点を、黒色で塗るか、白色で塗るかをメモ化再帰で求めていきます。
頂点を で塗ったときの場合の数
とします。(1が白、0が黒)
(1)頂点を白色に塗るとき(はの子の数)
の子は白、黒の2つの色に塗ることができるので
(2)頂点を黒色に塗るとき
の子は白色に塗ることができるので
ただし、が葉となる場合はとします。
#include<bits/stdc++.h> #define vi vector<int> #define vvi vector<vector<int> > #define vl vector<ll> #define vvl vector<vector<ll>> #define vb vector<bool> #define vc vector<char> #define vs vector<string> using ll = long long; using ld =long double; #define int ll #define INF 1e9 #define EPS 0.0000000001 #define rep(i,n) for(int i=0;i<n;i++) #define loop(i,s,n) for(int i=s;i<n;i++) #define all(in) in.begin(), in.end() template<class T, class S> void cmin(T &a, const S &b) { if (a > b)a = b; } template<class T, class S> void cmax(T &a, const S &b) { if (a < b)a = b; } #define MAX 9999999 using namespace std; typedef pair<int, int> pii; typedef pair<int,pii> piii; #define mp make_pair int MOD = INF+7; int memo[100005][2]; vector<vector<int>>edge; int dfs(int v,int from,int clr){ if(memo[v][clr]!=INF)return memo[v][clr]; memo[v][clr]=0; bool leaf = true; int value=1; rep(i,edge[v].size()){ int nv=edge[v][i]; if(nv == from)continue; leaf = false; if(clr){ value*=(dfs(nv,v,0)+dfs(nv,v,1)); value%= MOD; }else{ value*=dfs(nv,v,1); value%=(MOD); } } if(leaf)memo[v][clr]=1; else memo[v][clr]=value; return memo[v][clr]; } signed main(){ int n; cin>>n; edge.resize(n); rep(i,n-1){ int a,b; cin>>a>>b; --a,--b; edge[a].push_back(b); edge[b].push_back(a); } rep(i,n)rep(j,2)memo[i][j]=INF; cout<<(dfs(0,0,1)+dfs(0,0,0))%(MOD)<<endl; }
D - Mixing Experiment
D: Mixing Experiment - AtCoder Beginner Contest 054 | AtCoder
薬品をにする最小費用を求める問題。
制約も小さいので、でナップサックをすると解ける。
更新をを薬品の最小の比だと思い込んでて時間を溶かした。
具体的には、漸化式を
int div=(int)gcd((ll)(j+val[i+1].first),(ll)(k+val[i+1].second)); pii ni=mp((j+val[i+1].first)/div,(k+val[i+1].second)/div); cmin(dp[i+1][ni.first][ni.second],dp[i][j][k]+c[i+1]);
こんな形で更新してた。
頭を冷やしてみると、比の足し算はやばい事がわかるし、比はとの間にしか成り立たない関係ということもわかる。
反省。
#include<bits/stdc++.h> #define vi vector<int> #define vvi vector<vector<int> > #define vl vector<ll> #define vvl vector<vector<ll>> #define vb vector<bool> #define vc vector<char> #define vs vector<string> using ll = long long; using ld =long double; #define int ll #define INF 1e9 #define EPS 0.0000000001 #define rep(i,n) for(int i=0;i<n;i++) #define loop(i,s,n) for(int i=s;i<n;i++) #define all(in) in.begin(), in.end() template<class T, class S> void cmin(T &a, const S &b) { if (a > b)a = b; } template<class T, class S> void cmax(T &a, const S &b) { if (a < b)a = b; } #define MAX 9999999 using namespace std; typedef pair<int, int> pii; typedef pair<int,pii> piii; #define mp make_pair long long gcd(long long a, long long b) {return b ? gcd(b, a % b) : a;} long long lcm(long long m, long long n ){ if((0 == m )||( 0 == n ))return 0; return ((m / gcd(m, n)) * n); } int dp[41][401][401]; signed main(){ int n; cin>>n; pii comp; cin>>comp.first>>comp.second; rep(i,n)rep(j,401)rep(k,401)dp[i][j][k]=INF; dp[0][0][0]=0; vector<pii>val; vector<int>c(n); rep(i,n){ pii temp; cin>>temp.first>>temp.second; cin>>c[i]; val.push_back(mp(temp.first,temp.second)); dp[i][val[i].first][val[i].second]=c[i]; } rep(i,n-1){ rep(j,401){ rep(k,401){ if(dp[i][j][k]==INF)continue; cmin(dp[i+1][j][k],dp[i][j][k]); pii ni=mp((j+val[i+1].first),(k+val[i+1].second)); cmin(dp[i+1][ni.first][ni.second],dp[i][j][k]+c[i+1]); } } } int ans=INF; rep(i,401)rep(j,401){ int div=gcd(i,j); if(i&&j){ if(i/div==comp.first&&j/div==comp.second)cmin(ans,dp[n-1][i][j]); } } if(ans==INF)ans=-1; cout<<ans<<endl; }
D - 3N Numbers
D: 3N Numbers - AtCoder Beginner Contest 062 | AtCoder
以前解けなかった問題が、ヒントで解けるようになったので…
前半の要素と後半の要素の差を最大にすれば良い。
中間の要素(配列の添字だと)を最適に選べば良い。
PriorityQueueを使って、前半要素のうちの最小値と中間要素を比較していく。(後半の場合も同じく)
ちなみに、
Submission #1735282 - AtCoder Beginner Contest 062 | AtCoder
こういう解法だと絶対に解けない。要素全部前半にくっつけたほうが良い場合がある。
が奇数の時、前半と後半の両方に含まれる場合があるんじゃないかと思ってたけど、そんなことはない。
(↑これでめっちゃ時間とかした。)
#include<bits/stdc++.h> using ll = long long; #define int ll #define ld long double #define INF 1e9 #define EPS 0.0000000001 #define rep(i,n) for(int i=0;i<n;i++) #define all(in) in.begin(), in.end() template<class T, class S> void cmin(T &a, const S &b) { if (a > b)a = b; } template<class T, class S> void cmax(T &a, const S &b) { if (a < b)a = b; } #define MAX 9999999 using namespace std; typedef pair<int, int> pii; typedef pair<double,double>pdd; signed main(){ int n; cin>>n; vector<int>v(3*n); rep(i,3*n) cin>>v[i]; priority_queue<int,vector<int>,greater<int>>lque; priority_queue<int,vector<int>>sque; int ls=0,ss=0; vector<int>lv; vector<int>sv; rep(i,n){ lque.push(v[i]); sque.push(v[2*n+i]); ls+=v[i]; ss+=v[2*n+i]; } lv.push_back(ls); sv.push_back(ss); int ans=-INF*INF; cmax(ans,ls-ss); rep(i,n){ lque.push(v[n+i]); sque.push(v[2*n-i-1]); ls+=v[n+i]-lque.top(); ss+=v[2*n-i-1]-sque.top(); lv.push_back(ls); sv.push_back(ss); sque.pop(); lque.pop(); } rep(i,lv.size())cmax(ans, lv[i]-sv[n-i]); cout<<ans<<endl; }
番目まで見たときの、前半の合計の最大値
番目まで見たときの、後半の合計の最小値
添字マジックには気をつけよう!
ABC 051 D - Candidates of No Shortest Paths
D: Candidates of No Shortest Paths - AtCoder Beginner Contest 051 | AtCoder
無向連結グラフの最短距離
に含まれない辺の数を求める問題です。
含まれない辺 = 全点間最短経路を求めたときに、更新された辺
が含まれない辺ということがわかります。
の範囲はたかだたなので、warshallfloyd法で充分間に合います。
#include<bits/stdc++.h> using namespace std; #define rep(i,n) for(int i=0;i<n;i++) using pii = pair<int, int>; #define int ll #define INF 1e18 using ll = long long; signed main(){ int n,m; cin>>n>>m; vector<vector<int>>d(n,vector<int>(n,INF)); rep(i,m){ int a,b,c; cin>>a>>b>>c; d[a-1][b-1]=d[b-1][a-1]=c; } vector<vector<int>>d_min; rep(i,n)d[i][i]=0; d_min=d; rep(k,n)rep(i,n)rep(j,n)d_min[i][j]=min(d_min[i][j],d_min[i][k]+d_min[k][j]); int ans=0; rep(i,n)rep(j,n)if(i!=j&&d[i][j]!=INF&&d_min[i][j]!=INF&&d[i][j]!=d_min[i][j]) ans++; cout<<ans/2<<endl; return 0; }