B:Biking Duck
题意:现在有一个人要从(x1,y1)点走到(x2,y2)点, 现在走路的速度为v。 还有骑自行车的速度v2,自行车要从某个自行车站到另一个自行车站,现在我们可以视地图的边界都为自行车站,求最小时间是多少。
题解:对于忽视边界的问题,那就是暴力枚举。现在有了边界, 我们假设一个点在边界,那么我们可以枚举另一个自行车点之后,在某个边界上三分另一个点。 下一个情况就是2个自行车站都在边界上,我们三分套三分去维护找到最小值。
思维不怎么麻烦,就是写的有点麻烦。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<double,double> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; pll p[N], x, y, ld, ru; double v1, v2; int n; double dis(pll p1, pll p2){ return sqrt(pow(p1.fi-p2.fi,2)+pow(p1.se-p2.se,2)); } double cal(pll p1, pll p2, int fx){ if(fx == 1 || fx == 3){ double posy; if(fx == 1) posy = ld.se; else posy = ru.se; double lx = ld.fi, rx = ru.fi; for(int i = 0; i < 60; ++i){ double m = (lx+rx)/2.0; double mm = (m+rx)/2.0; if((dis(p1,{m,posy})/v2+dis(p2,{m,posy})/v1) > (dis(p1,{mm,posy})/v2+dis(p2,{mm,posy})/v1)) lx = m; else rx = mm; } return dis(p1,{lx,posy})/v2 + dis(p2,{lx,posy})/v1; } else { double posx; if(fx == 2) posx = ld.fi; else posx = ru.fi; double ly = ld.se, ry = ru.se; for(int i = 0; i < 60; ++i){ double m = (ly+ry)/2.0; double mm = (m+ry)/2.0; if(dis(p1,{posx,m})/v2 + dis(p2,{posx,m})/v1 > dis(p1,{posx,mm})/v2 + dis(p2,{posx,mm})/v1) ly = m; else ry = mm; } return dis(p1,{posx,ly})/v2 + dis(p2,{posx,ly})/v1; } } double solve(int fx, int ffx){ if(fx == 1 || fx == 3){ double posy; if(fx == 1) posy = ld.se; else posy = ru.se; double lx = ld.fi, rx = ru.fi; for(int i = 0; i < 60; ++i){ double m = (lx+rx)/2.0; double mm = (m+rx)/2.0; if(dis(x,{m,posy})/v1+cal({m,posy},y,ffx) > dis(x,{mm,posy})/v1+cal({mm,posy},y,ffx)) lx = m; else rx = mm; } return dis(x,{lx,posy})/v1+cal({lx,posy},y,ffx); } else { double posx; if(fx == 2) posx = ld.fi; else posx = ru.fi; double ly = ld.se, ry = ru.se; for(int i = 0; i < 60; ++i){ double m = (ly+ry)/2.0; double mm = (m+ry)/2.0; if(dis(x,{posx,m})/v1+cal({posx,m},y,ffx) > dis(x,{posx,mm})/v1+cal({posx,mm},y,ffx)) ly = m; else ry = mm; } return dis(x,{posx,ly})/v1+cal({posx,ly},y,ffx); } } int main(){ scanf("%lf%lf", &v1, &v2); scanf("%lf%lf", &ld.fi, &ld.se); scanf("%lf%lf", &ru.fi, &ru.se); scanf("%lf%lf", &x.fi, &x.se); scanf("%lf%lf", &y.fi, &y.se); scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%lf%lf", &p[i].fi, &p[i].se); double ans = dis(x,y)/v1; for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j){ double tmp = dis(p[i],p[j])/v2+(dis(p[i],x)+dis(p[j],y))/v1; ans = min(ans, tmp); } for(int i = 1; i <= n; ++i){ ans = min(ans, dis(x,p[i])/v1+cal(p[i],y,1)); ans = min(ans, dis(x,p[i])/v1+cal(p[i],y,2)); ans = min(ans, dis(x,p[i])/v1+cal(p[i],y,3)); ans = min(ans, dis(x,p[i])/v1+cal(p[i],y,4)); ans = min(ans, dis(y,p[i])/v1+cal(p[i],x,1)); ans = min(ans, dis(y,p[i])/v1+cal(p[i],x,2)); ans = min(ans, dis(y,p[i])/v1+cal(p[i],x,3)); ans = min(ans, dis(y,p[i])/v1+cal(p[i],x,4)); } for(int i = 1; i <= 4; ++i){ for(int j = 1; j <= 4; ++j){ ans = min(ans, solve(i,j)); } } printf("%.10f ", ans); return 0; }
C:Cent Savings
题意:将传送带上的物品最多分成m+1个部分,每部分单独付费这个付费是四舍五入,然后问最小花费是多少。
题解: DP跑一下花费。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 2e3 + 100; int a[N], b[N]; int dp[N][200]; int main(){ int n, m; int ans = 0; memset(dp, inf, sizeof(dp)); scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i){ scanf("%d", &a[i]); ans += a[i] / 10; a[i] %= 10; b[i] = b[i-1] + a[i]; } ans *= 10; dp[0][0] = 0; for(int i = 1; i <= n; ++i){ for(int l = 1; l <= i; ++l){ int sum = b[i] - b[l-1]; int t = sum % 10; if(t > 4) sum = sum + 10 - t; else sum -= t; for(int k = 0; k <= m; ++k) dp[i][k+1] = min(dp[i][k+1], sum + dp[l-1][k]); } } int fans = inf; for(int i = 0; i <= m+1; ++i){ fans = min(fans, dp[n][i]); } cout << ans+fans << endl; return 0; }
D:Digi Comp II
题意:现在有N个板子,每个板子都有一个朝向,每次一个求落在板子上,求会掉到现在的方向的这一边,然后板子换一个方向,现在落下n个球,问最后所有板子的方向。
题解:模拟, 按照toposort的方式处理就好了。 注意就是左边和右边都有可能是指向同一个新的板子。
所以不能先把所有板子都 --度数之后,再入队。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 5e5 + 100; LL a[N]; LL n; int vis[N], m, d[N]; int to[N][2]; void toposort(){ queue<int> q; for(int i = 1; i <= m; ++i){ if(!d[i]) q.push(i); } while(!q.empty()){ int x = q.front(); q.pop(); a[to[x][0]] += a[x]/2; a[to[x][1]] += a[x]/2; if(a[x]&1){ a[to[x][vis[x]]]++; vis[x] ^= 1; } --d[to[x][0]]; if(d[to[x][0]] == 0) q.push(to[x][0]); --d[to[x][1]]; if(d[to[x][1]] == 0) q.push(to[x][1]); } } int main(){ scanf("%lld%d", &n, &m); char op[3]; for(int i = 1; i <= m; ++i){ scanf("%s", op); if(op[0] == 'L') vis[i] = 0; else vis[i] = 1; scanf("%d%d", &to[i][0], &to[i][1]); ++d[to[i][0]]; ++d[to[i][1]]; } a[1] = n; toposort(); for(int i = 1; i <= m; ++i) printf("%c","LR"[vis[i]]); return 0; }
E:Euclidean TSP
题意:找到一个的c使得题目中的2个式子之和最小。
题解:3分答案,这一定是一个凹函数。
代码:
1 #include<bits/stdc++.h> 2 #define fio ios::sync_with_stdio(false);cin.tie(0) 3 using namespace std; 4 5 6 double n, p, s, v; 7 double f(double c) { 8 double t1 = (n * pow(log(n) / log(2), c * sqrt(2))) / (p * 1e9); 9 double t2 = s * (1.0 + 1.0 / c) / v; 10 return t1 + t2; 11 } 12 int main() { 13 scanf("%lf%lf%lf%lf", &n, &p, &s, &v); 14 double l = 0, r = 1e30; 15 for (int i = 0; i < 1e4; i++) { 16 double ll = (l + r) / 2; 17 double rr = (ll + r) / 2; 18 if (f(ll) > f(rr)) l = ll; 19 else r = rr; 20 } 21 printf("%f %f ", f(l), l); 22 }
F:Finding Lines
题意:给你n个点,然后在让你添加上p个点,问你能不能满足至少一条直线上出现 (n+3)/4 个点。
题解:随机生成p个点500次, 然后check。
代码:
#include<bits/stdc++.h> #define fio ios::sync_with_stdio(false);cin.tie(0) using namespace std; const int N = 1e5 + 10; typedef long long ll; struct point { ll x, y; point (ll _x = 0, ll _y = 0) :x(_x), y(_y) {} void read() { scanf("%I64d%I64d", &x, &y); } void out() { cout<<"x = "<<x<<" y = "<<y<<endl; } point operator - (const point &rhs) const { return point(x - rhs.x, y - rhs.y); } }p[N]; ll cross(point a, point b) { return a.x * b.y - a.y * b.x; } int n, percent, need; int main() { srand(time(NULL)); scanf("%d%d", &n, &percent); for (int i = 0; i < n; i++) p[i].read(); if (n <= 2) { puts("possible"); return 0; } int need = (n * percent) / 100; if ((n * percent) % 100 != 0) need++; int mx = 0; for (int _ = 0; _ < 500; _++) { random_shuffle(p, p + n); int cnt = 2; for (int i = 2; i < n; i++) if (cross(p[0] - p[1], p[i] - p[0]) == 0) cnt++; if (cnt >= need) { puts("possible"); return 0; } mx = max(mx, cnt); } puts("impossible"); return 0; }
G:Gathering
题意:在平面上有n个点,然后现在让你找到一个点,使得所有点到这个点的曼哈顿距离只和最小,且不能有距离>d的点。
题解:如果我们不考虑距离 > d的情况下, 那么最优解肯定就是所有点的x值的中位数, y值的中位数。
现在我们多了一个限制性的条件, 那么我们先把这个可行域扣出来,可行域是一个斜着的正方形,那么最优解的那个点如果落在可行域之内,那么就是这个点, 但是如果落在外面的话,就会存在一种现象,如果当x更靠近最优解的点的时候, y可能会变得更远。
对于上面的那个东西来说, 他会形成一个凹函数, 这个地方我们可以用三分去找到答案。
我们每次三分x轴, 当x轴定下来之后,那么y就自然是选择可行范围内的位置中更靠近最优解的点,所以只需要一个三分就好了。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; int x[N], y[N]; LL A = INF, B = -INF, C = -INF, D = INF; LL midy; int n; LL cal(LL xx){ LL uy = min(xx-C, A-xx); LL dy = max(B-xx, xx-D); if(uy < dy) return INF; LL ty; if(midy > uy) ty = uy; else if(midy < dy) ty = dy; else ty = midy; LL ret = 0; for(int i = 1; i <= n; ++i){ ret += abs(x[i]-xx) + abs(y[i]-ty); } return ret; } int main(){ scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d%d", &x[i], &y[i]); LL d; scanf("%lld", &d); for(int i = 1; i <= n; ++i){ A = min(A, x[i]+y[i]+d); B = max(B, x[i]+y[i]-d); C = max(C, x[i]-y[i]-d); D = min(D, x[i]-y[i]+d); } //cout << A << ' ' << B << ' '<< C << ' ' << D << endl; if(A < B || C > D) { puts("impossible"); return 0; } sort(y+1, y+1+n); midy = y[(n+1)/2]; LL lx = (C+B)/2, rx = (A+D)/2; //cout << lx << ' ' << rx << endl; while(lx + 2 < rx){ LL lm = (lx+rx)/2 , rm = (lm+rx)/2; if(cal(lm) < cal(rm)) rx = rm; else lx = lm; } LL ans = INF; for(lx; lx <= rx; ++lx) ans = min(ans, cal(lx)); printf("%lld ", ans); return 0; }
H:Hyacinth
题意:给你一棵树,对于这棵树来说,每一个端点来说是有2个频率,任意2个相连的点,至少要有一个频率是相同的,一个频率如果有效,那么至少出现在一对相邻的点上,求一种构造方式使得有效频率最多。
题解:先找到一个根,然后对于根来说随意分配2个值,然后对于一个新的点来说,先找到父亲的2个频率没出现2次的,找不到就随便一个,然后如果这个点有儿子,那么就来一个新的频率,如果没有儿子就把这个点的另一个频率赋值给这个点。
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; pll p[N]; vector<int> vc[N]; int tot; int vis[N]; queue<int> q; void dfs(int o, int u){ if(u == 1) { p[u].fi = 1; p[u].se = 2; vis[1] = vis[2] = 1; tot = 3; for(auto v : vc[u]){ dfs(u, v); } } else { if(vc[u].size() == 1){ p[u] = p[o]; ++vis[p[u].fi]; ++vis[p[u].se]; return ; } if(vis[p[o].fi] >= 2) p[u].fi = p[o].se; else p[u].fi = p[o].fi; vis[p[u].fi]++; p[u].se = tot++; vis[p[u].se]++; for(auto v : vc[u]){ if(v == o) continue; dfs(u, v); } } } int pp = 1; int main(){ int n; scanf("%d", &n); for(int i = 1, u, v; i < n; ++i){ scanf("%d%d", &u, &v); vc[u].pb(v); vc[v].pb(u); } dfs(0,1); for(int i = 1; i <= n; ++i){ printf("%d %d ", p[i].fi, p[i].se); } return 0; }
I:Indoorienteering
题意:有一副 n 个点的完全图,问你是否存在某个路使得,从某点出发,跑一圈会到这个点,其他点都恰好经过一次,且总路程为L。
题解:对于这个东西,我们把这幅图割成2部分,1号点一定在左边,并且一定是这幅图的起点,我们枚举左边图的状态, 枚举左边图的另一个端点,然后爆搜右边图的状态,然后在爆搜左边图的确定端点之后的状态,然后二分check。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 20; LL d[N][N], l; int lf[N], lsz, rt[N], rsz; LL vc[100000], vsz; void cal(int b, int e){ vsz = 0; do{ LL cnt = 0; cnt += d[b][rt[1]] + d[e][rt[rsz]]; for(int i = 2; i <= rsz; ++i) cnt += d[rt[i-1]][rt[i]]; vc[++vsz] = cnt; }while(next_permutation(rt+1,rt+1+rsz)); sort(vc+1, vc+1+vsz); vsz = unique(vc+1, vc+1+vsz) - vc - 1; } int c[N], csz; inline bool bck(LL v){ if(v <= 0) return false; int p = lower_bound(vc+1, vc+1+vsz, v) - vc; if(vc[p] == v) return true; return false; } void check(int u, int v){ csz = 0; for(int i = 1; i <= lsz; ++i){ if(lf[i] == u || lf[i] == v) continue; c[++csz] = lf[i]; } if(csz){ do{ LL tmp = l; tmp -= d[u][c[1]] + d[v][c[csz]]; for(int i = 2; i <= csz; ++i){ tmp -= d[c[i-1]][c[i]]; } if(bck(tmp)){ puts("possible"); exit(0); } }while(next_permutation(c+1, c+1+csz)); } else{ if(bck(l-d[u][v])){ puts("possible"); exit(0); } } } void solve(){ int t = -1; for(int i = 2; i <= lsz; ++i){ t = lf[i]; cal(1,t); check(1,t); } if(t == -1) { cal(1,1); check(1,1); } } int main(){ int n; scanf("%d%lld", &n, &l); for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) scanf("%lld", &d[i][j]); int lnum = n/2 , rnum = n - lnum; int m = (1<<n); for(int i = 0; i < m; ++i){ lsz = rsz = 0; for(int j = 0; j < n; ++j){ if((i>>j)&1) lf[++lsz] = j; else rt[++rsz] = j; } if(lsz == lnum && lf[1] == 1) solve(); } puts("impossible"); return 0; }
J:Judging Troubles
代码:
#include<bits/stdc++.h> #define fio ios::sync_with_stdio(false);cin.tie(0) using namespace std; map<string, int>mp1, mp2; int n, ans; int main() { fio; cin>>n; string tmp; for (int i = 0; i < n; i++) { cin>>tmp; mp1[tmp]++; } for (int i = 0; i < n; i++) { cin>>tmp; mp2[tmp]++; } for (auto it = mp1.begin(); it != mp1.end(); ++it) { ans += min(it->second, mp2[it->first]); } cout<<ans<<endl; }
K:Knapsack Collection
题意:现在有一个长度为s的传送带,传送带上有n个物品,每个物品的位置在pi,现在每次拿一个物品要t秒,如果你站在传送带的位置上,那么你就会有东西传到你的面前,然后你就会用t的时间去拿这个东西。现在对于传送带的每个位置,你都会有一个拿完所有的物品的花费时间, 现在问你这个花费时间最小是多少, 最大是多少, 平均时间是多少。
题解:我们枚举所有从所有有东西的位置开始,最小的时间一定会是从某个有东西的位置开始的,然后最大的时间一定是某个有东西的位置的后面,我们算出所有有东西的状态,然后上一个位置如果没有东西,就是这个位置的状态+1。
然后我们就模拟拿东西的过程,我们可以把自己不动传送带动的方式 -> 传送带不动 自己动来模拟。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 2100; int cnt[N]; int a[N]; LL tim[N]; int n, s, t; LL cal(int x){ LL ret = 0; int p = x; multiset<int> st; multiset<int>::iterator it; for(int i = 1; i <= n; ++i) st.insert(a[i]); while(!st.empty()){ it = st.lower_bound(p); if(it == st.end()){ ret += s - p; p = 0; it = st.lower_bound(p); } ret += (*it) - p + t; p = ((*it)+t) % s; st.erase(it); } return ret; } int main(){ scanf("%d%d%d", &n, &s, &t); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); sort(a+1, a+1+n); a[0] = -2; LL sum = 0; for(int i = 1; i <= n; ++i){ if(a[i] == a[i-1]) tim[i] = tim[i-1]; else { tim[i] = cal(a[i]); //sum += tim[i]; } } LL mn = tim[1], mx = tim[1]; for(int i = 2; i <= n; ++i){ mn = min(mn, tim[i]); mx = max(mx, tim[i]); } for(int i = 1; i <= n; ++i){ if(a[i] == a[i-1]) continue; int l = a[i-1] + 1, r = a[i]; if(l == -1) l = a[n] + 1, r += s; //cout << l << ' ' << r << endl; int len = r - l + 1; mx = max(mx, tim[i]+len-1); sum += 1ll * (tim[i]+tim[i]+len-1) * len / 2; } cout << mn << endl; cout << mx << endl; LL gcd = __gcd(sum, 1ll*s); sum /= gcd; s /= gcd; cout << sum << '/' << s << endl; return 0; }