Codeforces Round #367 (Div. 2)
A. Beru-taxi
問題文
解法
シミュレーションする。
感想
やるだけ。
コード
#include <bits/stdc++.h> #define rep(i,n) for(int i = 0; i < n; i++) const double PI = 3.1415926535897932384626433832795028841971; const int INF = 100000000; const double EPS = 1e-10; const int MOD = 1000000007; using namespace std; typedef long long ll; typedef pair<int,int> P; double a, b; int n; double ans = 1000000.0; int main(){ cin >> a >> b; cin >> n; rep(i,n){ double x, y, v; cin >> x >> y >> v; ans = min(ans,sqrt((a-x)*(a-x)+(b-y)*(b-y))/v); } printf("%.9f\n",ans); }
B. Interesting drink
問題文
解法
飲み物の値段でソートしてクエリごとに二分探索をするだけ。
感想
やるだけ。
コード
#include <bits/stdc++.h> #define rep(i,n) for(int i = 0; i < n; i++) const double PI = 3.1415926535897932384626433832795028841971; const int INF = 1000000009; const double EPS = 1e-10; const int MOD = 1000000007; using namespace std; typedef long long ll; typedef pair<int,int> P; int n; int x[100001]; int q; int main(){ scanf("%d",&n); rep(i,n) scanf("%d",x+i); sort(x,x+n); x[n] = INF; scanf("%d",&q); rep(i,q){ int m; scanf("%d",&m); int s = 0, e = n, mid; while(e-s > 1){ mid = (e+s)/2; if(x[mid] <= m) s = mid; else e = mid; } if(x[0] > m) printf("0\n"); else printf("%d\n",s+1); } }
C. Hard problem
問題文
解法
dp[n][反転させるかどうか]のdpをするだけ。
感想
やるだけ。
コード
#include <bits/stdc++.h> #define rep(i,n) for(int i = 0; i < n; i++) const double PI = 3.1415926535897932384626433832795028841971; const long long int INF = 1000000000000000; const double EPS = 1e-10; const int MOD = 1000000007; using namespace std; typedef long long ll; typedef pair<int,int> P; int n; ll c[100000]; string str[100000], rev[100000]; ll dp[100000][2]; int main(){ cin.tie(0); ios::sync_with_stdio(false); cin >> n; rep(i,n) cin >> c[i]; rep(i,n) cin >> str[i]; rep(i,n){ rev[i] = str[i]; reverse(rev[i].begin(),rev[i].end()); } rep(i,n) rep(j,2) dp[i][j] = INF; dp[0][0] = 0; dp[0][1] = c[0]; for(int i = 1; i < n; i++){ if(str[i-1] <= str[i]){ dp[i][0] = min(dp[i][0],dp[i-1][0]); } if(rev[i-1] <= str[i]){ dp[i][0] = min(dp[i][0],dp[i-1][1]); } if(str[i-1] <= rev[i]){ dp[i][1] = min(dp[i][1],dp[i-1][0]+c[i]); } if(rev[i-1] <= rev[i]){ dp[i][1] = min(dp[i][1],dp[i-1][1]+c[i]); } } ll ans = min(dp[n-1][0],dp[n-1][1]); if(ans == INF) ans = -1; cout << ans << endl; }
D. Vasiliy's Multiset
問題文
解法
各xiを二分木の葉に対応させてそこに出現回数を記録する。
具体的には、xiを上位bitから見ていき、フラグが立っていれば右の子に、フラグが立っていなければ左の子にと遷移していけばよい。
そのような二分木を構築すると、"? x"クエリが来た時、xorを最大化するためには右の子と左の子のどちらに移動するべきかがxのbitを見ればわかるので解ける。
感想
そこまで難しい発想ではないんだけどコンテスト中に解けてよかった。
コード
#include <bits/stdc++.h> #define rep(i,n) for(int i = 0; i < n; i++) const double PI = 3.1415926535897932384626433832795028841971; const int INF = 100000000; const double EPS = 1e-10; const int MOD = 1000000007; using namespace std; typedef long long ll; typedef pair<int,int> P; struct v{ int ex; int dep; int r, l; int number; } vv[10000000]; int q; int counter = 1; int p[30]; void init(){ rep(i,30) p[i] = (1<<i); } void add(int x, int dep, int num){ vv[num].ex++; if(dep == 0){ vv[num].number = x; return; } if(x&p[dep-1]){ if(vv[num].r == -1){ vv[num].r = counter; vv[counter].dep = dep-1; counter++; } add(x,dep-1,vv[num].r); } else{ if(vv[num].l == -1){ vv[num].l = counter; vv[counter].dep = dep-1; counter++; } add(x,dep-1,vv[num].l); } } void del(int x, int dep, int num){ vv[num].ex--; if(dep == 0) return; if(x&p[dep-1]){ del(x,dep-1,vv[num].r); } else{ del(x,dep-1,vv[num].l); } } int que(int x, int dep, int num){ int ret = x; if(vv[num].ex == 0) return x; if(dep == 0) return x^vv[num].number; if(x&p[dep-1]){ if(vv[num].l != -1 && vv[vv[num].l].ex != 0) ret = max(ret,que(x,dep-1,vv[num].l)); else ret = max(ret,que(x,dep-1,vv[num].r)); } else{ if(vv[num].r != -1 && vv[vv[num].r].ex != 0) ret = max(ret,que(x,dep-1,vv[num].r)); else ret = max(ret,que(x,dep-1,vv[num].l)); } return ret; } int main(){ init(); cin >> q; rep(i,10000000){ vv[i].r = -1; vv[i].l = -1; vv[i].ex = 0;} vv[0].dep = 30; add(0,30,0); rep(i,q){ char a; int b; scanf(" %c%d",&a,&b); if(a == '+') add(b,30,0); if(a == '-') del(b,30,0); if(a == '?') printf("%d\n",que(b,30,0)); } }
E. Working routine
問題文
解法
全ての隣り合ってるセル間に辺をはり、クエリが飛んで来たらswapされる四角形の周囲のセルについてのみ辺を張りかえれば計算量がO(q*(n+m))となって解ける。
n*mの四角形の外に番兵的な用途でマスを追加しておくと実装が楽になる。
感想
わからなかったのでeditorialとか人のコードとか読んで通した。
解法自体は典型なのだが(思いつかなかったので猛省だけど)、辺を張りかえるという部分でやたらバグらせてしまった。
コード
#include <bits/stdc++.h> #define rep(i,n) for(int i = 0; i < n; i++) const double PI = 3.1415926535897932384626433832795028841971; const int INF = 100000000; const double EPS = 1e-10; const int MOD = 1000000007; using namespace std; typedef long long ll; typedef pair<int,int> P; struct ver{ int r, d; int val; } v[1500000]; int n, m, q; int find(int id, int h, int w){ int ret = id; rep(i,h) ret = v[ret].d; rep(i,w) ret = v[ret].r; return ret; } int main(){ scanf("%d%d%d",&n,&m,&q); rep(i,n) rep(j,m){ scanf("%d",&v[(i+1)*(m+1)+j+1].val); } rep(i,n+1) rep(j,m+1){ if(i != n) v[i*(m+1)+j].d = (i+1)*(m+1)+j; else v[i*(m+1)+j].d = -1; if(j != m) v[i*(m+1)+j].r = i*(m+1)+j+1; else v[i*(m+1)+j].r = -1; } rep(i,q){ int a, b, c, d, h, w; scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&h,&w); int l1 = find(0,a,b-1); int l2 = find(l1,0,w); int u1 = find(0,a-1,b); int u2 = find(u1,h,0); int L1 = find(0,c,d-1); int L2 = find(L1,0,w); int U1 = find(0,c-1,d); int U2 = find(U1,h,0); rep(j,h){ swap(v[l1].r,v[L1].r); swap(v[l2].r,v[L2].r); l1 = v[l1].d; L1 = v[L1].d; l2 = v[l2].d; L2 = v[L2].d; } rep(j,w){ swap(v[u1].d,v[U1].d); swap(v[u2].d,v[U2].d); u1 = v[u1].r; U1 = v[U1].r; u2 = v[u2].r; U2 = v[U2].r; } } int st = find(0,1,1); rep(i,n){ int now = st; rep(j,m){ if(j == 0) printf("%d",v[now].val); else printf(" %d",v[now].val); now = v[now].r; } printf("\n"); st = v[st].d; } }