2015-2016 ACM ICPC Baltic Selection Contest [解题报告]
2015-11-11 21:27
573 查看
新地址链接
放上一个max开关或者一个min开关, 如果u是max开关,那么val[u] =
max(val[v]), v是u的子节点,其中min开关和max开关的个数都是给定的,
并且max开关+min开关 = n-叶子节点数,让你安排每个中间节点是max还是min,
输出根节点的最大值和最小值
首先,我们先考虑把最大的叶子节点u传上去,因为u最大,所以,我们
在往上的路径分叉处只能用max开关,为了能让u传上去,肯定应该让不是该路径上的
其他分叉都尽量用min开关以节省max开关,现在我们考虑u的兄弟节点v,如果要把u的兄弟节点
v传上去,因为u比v大,所以我们在u和v的父节点f上肯定用min开关最划算,那么现在我们就把
问题转化成了把f上的节点传到根节点去(即min(u,v))。
所以说:如果当前考虑的节点u够大,那么我们就直接让接下来的路程都用max开关,否则就让他
用min开关传到父节点去。
代码:
奇数步的花费为a,偶数步的花费为b, 直接先求从左上角到右下角的最短路step
然后答案就是step/2*(a+b) + (step&1),因为第一步是奇数步。
代码:
建图的时候建立从小点到大点建有向图,然后从小到大扫描每个点,用
树状数组维护即可。
代码:
代码:
看是否能放进去就行了。
二是B:从队尾进入一个数,三是第O i: 第i个进入的数选择两边中人少的一边出队,
所有在他前方的依次出队,从队列的另一边依次进入,输出地i个人出队的时候,有多少人
要因此出队。用Splay模拟即可
代码:
因为20的阶乘就已经超过了1e18,直接把所有的组合情况放进set,然后二分查找即可。
A题:AHB 水题,直接上代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long Long; const int maxn = 1e5 + 10; const int maxm = 256; int main() { char s1[20], s2[20]; while (scanf("%s%s", s1, s2) != EOF) { int l1 = (int) strlen(s1), l2 = (int) strlen(s2); bool flag = false; for (int i = 0; i < l1; i++) { int v1 = s1[i]-'0', v2 = s2[i]-'0'; int v = max(v1,v2)-min(v1,v2); if (v == 0 && !flag) { continue; } flag = true; printf("%d", v); } if (!flag) printf("0"); puts(""); } return 0;
B题:Wet Boxes 这个题据说是线段树,暂时没做出来
C题: Minimax Tree
题意:
给出一棵n个节点的树,叶节点都有一个value值,其余节点每个节点可以放上一个max开关或者一个min开关, 如果u是max开关,那么val[u] =
max(val[v]), v是u的子节点,其中min开关和max开关的个数都是给定的,
并且max开关+min开关 = n-叶子节点数,让你安排每个中间节点是max还是min,
输出根节点的最大值和最小值
题解:
因为求最大值和最小值都是一样的方法,所以我们以求最大值来分析,首先,我们先考虑把最大的叶子节点u传上去,因为u最大,所以,我们
在往上的路径分叉处只能用max开关,为了能让u传上去,肯定应该让不是该路径上的
其他分叉都尽量用min开关以节省max开关,现在我们考虑u的兄弟节点v,如果要把u的兄弟节点
v传上去,因为u比v大,所以我们在u和v的父节点f上肯定用min开关最划算,那么现在我们就把
问题转化成了把f上的节点传到根节点去(即min(u,v))。
所以说:如果当前考虑的节点u够大,那么我们就直接让接下来的路程都用max开关,否则就让他
用min开关传到父节点去。
代码:
#include <cmath> #include <queue> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define vsz(u) ((int) G[u].size()) typedef long long Long; typedef vector<int>::iterator vIte; const int maxn = 1e5 + 10; const int inf = 1e9; const double eps = 1e-8; vector<int> G[maxn]; int val[maxn], minv[maxn], maxv[maxn]; int ansmn, ansmx, mn, mx, lev; void dfs(int u, int judge) { minv[u] = inf; maxv[u] = 0; if (vsz(u) == 0) { minv[u] = maxv[u] = val[u]; } for (vIte it = G[u].begin(); it != G[u].end(); ++it) { dfs(*it, judge+(vsz(u)!=1)); minv[u] = min(minv[u], minv[*it]); maxv[u] = max(maxv[u], maxv[*it]); } if (judge <= mx) ansmx = max(ansmx, minv[u]); if (judge <= mn) ansmn = min(ansmn, maxv[u]); } int main() { int n, k; while (scanf("%d%d",&n, &k) != EOF) { for (int i = 0; i < maxn; i++) { G[i].clear(); } for (int i = 2; i <= n; i++) { int p; scanf("%d", &p); G[p].push_back(i); } int zero = 0; for (int i = 1; i <= n; i++) { scanf("%d", &val[i]); zero += !val[i]; } mn = k, mx = zero-k, lev = n-zero; ansmn = inf; ansmx = 0; dfs(1, 0); printf("%d %d\n", ansmn, ansmx); } return 0; }
D题:Journey
给一个n*m的矩阵,由.和#组成,要从左上角走到右下角的花费,其中奇数步的花费为a,偶数步的花费为b, 直接先求从左上角到右下角的最短路step
然后答案就是step/2*(a+b) + (step&1),因为第一步是奇数步。
代码:
#include <iostream> #include <cstdio> #include <queue> #include <cstring> using namespace std; const int maxn = 507; int n, m; int dire[][2] = { {-1,0},{1,0},{0,-1},{0,1} }; char mapp[maxn][maxn]; bool vis[maxn][maxn]; struct Data { int x, y, step; Data() {} Data(int x1, int y1, int step1) {x = x1, y = y1, step = step1;} }cur; queue<Data> que; bool safe(int x, int y) { if (0 <= x && x < n && 0 <= y && y < m) return true; return false; } int bfs() { memset(vis, false, sizeof(vis)); while (!que.empty()) que.pop(); que.push(Data(0,0,0)); vis[0][0] = true; while (!que.empty()) { cur = que.front(); que.pop(); if (cur.x == n-1 && cur.y == m-1) return cur.step; for (int i = 0; i < 4; i++) { int x = cur.x + dire[i][0], y = cur.y + dire[i][1]; if (safe(x, y)) { if (mapp[x][y] != '#' && !vis[x][y]) { que.push(Data(x,y,cur.step+1)); vis[x][y] = true; } } } } return -1; } int main() { while (scanf("%d%d", &m, &n) != EOF) { int a, b; scanf("%d%d", &a, &b); for (int i = 0; i < n; i++) { scanf("%s", mapp[i]); } int ans = bfs(); if (ans == -1) printf("IMPOSSIBLE\n"); else { int cost; cost = (ans / 2) * (a + b); if (ans & 1) cost += b; printf("%d\n", cost); } } return 0; }
E题:Permutation Polygon
给一个n个点,点是1-n顺时针的,n条边的图,问有多少个交点。建图的时候建立从小点到大点建有向图,然后从小到大扫描每个点,用
树状数组维护即可。
#include <cstdio> #include <vector> #include <cstring> #include <algorithm> using namespace std; #define pb push_back const int maxn = 1e5 + 10; typedef long long Long; typedef vector<int>::iterator vIte; Long C[maxn]; int n; vector<int> G[maxn]; inline int lowbit(int x) { return x&(-x); } void add(int x, int v) { while (x <= n) { C[x] += v; x += lowbit(x); } } Long sum(int p) { Long ret = 0; while (p > 0) { ret += C[p]; p -= lowbit(p); } return ret; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { int v; scanf("%d", &v); G[min(i,v)].pb(max(i,v)); } Long ans = 0; for (int i = 1; i <= n; i++) { add(i,-sum(i)); for (vIte it = G[i].begin(); it != G[i].end(); ++it) { ans += sum((*it)-1); } for (vIte it = G[i].begin(); it != G[i].end(); ++it) { add(*it,1); } } printf("%I64d\n", ans); return 0; }
F题:Unusual Sum
水题:直接化简一下就ok了:代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long Long; const int maxn = 1e5 + 10; const int maxm = 256; int main() { int t; scanf("%d", &t); while (t--) { double x, y; scanf("%lf%lf", &x, &y); printf("%.10f\n", (y+1-x)/(x*(y+1))); } return 0; }
G题:Robot Walk
水模拟:代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define maxn 100005 char str[maxn]; char op[maxn]; char ans[maxn]; int acnt = 0; int main() { int n, m, x; scanf("%d%d", &n, &x); scanf("%s", str); scanf("%d", &m); scanf("%s", op); x--; ans[acnt++] = str[x]; for (int i = 0; op[i] != '\0'; i++) { if (op[i] == 'L') x--; else x++; ans[acnt++] = str[x]; } ans[acnt] = '\0'; puts(ans); return 0; }
H题:Game of Corners
水题有点多:#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int main() { int n, m; scanf("%d%d", &n, &m); if (n > m) swap(n, m); printf("%I64d\n", (long long)n*(m+1)); return 0; }
I题:Shell Game
用前视图看,题目就转化为在一个梯形里面放圆,然后直接二分圆的半径,看是否能放进去就行了。
#include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long Long; const int maxn = 5e2 + 10; const double eps = 1e-8; bool ok(double m, double r, double R, double h) { r*=2; R*=2; double seta = atan(h/((R-r)/2.0)); double afa = atan(m/(R/2.0)); double bata = seta - afa; double edge = sqrt(m*m+(R/2.0)*(R/2.0)); double ans = edge*sin(bata); return ans >= m; } int main() { double r, R, h; while (scanf("%lf%lf%lf", &r, &R, &h) != EOF) { double low = 0, high = h/2.0; while (high-low >= eps) { double mid = (low+high) / 2.0; if (ok(mid,r,R,h)) { low = mid; } else high = mid; } printf("%.7f\n", low); } return 0; }
J题:Narrow Bus
给一个空队列,有n个操作,(1<=n<=1e5), 一是F:从队首进入一个数,二是B:从队尾进入一个数,三是第O i: 第i个进入的数选择两边中人少的一边出队,
所有在他前方的依次出队,从队列的另一边依次进入,输出地i个人出队的时候,有多少人
要因此出队。用Splay模拟即可
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1e5 + 10; const int inf = 1e8; struct node { int l, r, f, sz, v; void clear() { l = r = f = sz = 0; } } nd[maxn]; int rt, tot; void init() { tot = 0; rt = 0; } int newNode() { nd[++tot].clear(); return tot; } void up(int x) { nd[x].sz = 1; if (nd[x].l) nd[x].sz += nd[nd[x].l].sz; if (nd[x].r) nd[x].sz += nd[nd[x].r].sz; } void r_rorate(int x) { int f = nd[x].f, l = nd[x].l; if (nd[f].l == x) nd[f].l = l; else nd[f].r = l; nd[l].f = f; nd[x].l = nd[l].r; nd[nd[x].l].f = x; nd[x].f = l; nd[l].r = x; up(x); } void l_rorate(int x) { int f = nd[x].f, r = nd[x].r; if (nd[f].l == x) nd[f].l = r; else nd[f].r = r; nd[r].f = f; nd[x].r = nd[r].l; nd[nd[x].r].f = x; nd[x].f = r; nd[r].l = x; up(x); } void splay(int x, int rootf) { for (; nd[x].f != rootf; ) { int f = nd[x].f, g = nd[f].f; if (g == rootf) { if (nd[f].l == x) r_rorate(f); else l_rorate(f); } else { if (nd[g].l == f) if (nd[f].l==x) r_rorate(g), r_rorate(f); else l_rorate(f), r_rorate(g); else if (nd[f].r == x) l_rorate(g), l_rorate(f); else r_rorate(f), l_rorate(g); } } up(x); if (!rootf) rt = x; } int getFirst(int x) { for (; nd[x].l; x = nd[x].l) ; return x; } int getLast(int x) { for (; nd[x].r; x = nd[x].r) ; return x; } int Gao(int x) { splay(x, 0); int ret = inf; if (nd[x].l) ret = min(ret, nd[nd[x].l].sz); else ret = 0; if (nd[x].r) ret = min(ret, nd[nd[x].r].sz); else ret = 0; if (nd[x].r == 0) { nd[nd[x].l].f = 0; rt = nd[x].l; nd[x].l = 0; return ret; } nd[nd[x].r].f = 0; int xx = getLast(x); splay(xx, 0); nd[xx].r = nd[x].l; if (nd[x].l) nd[nd[x].l].f = xx; if (nd[x].l) splay(nd[x].l, 0); nd[x].r = 0; return ret; } int main() { int n; while (scanf("%d", &n) != EOF) { init(); nd[0].clear(); for (int i = 0; i < n; i++) { nd[0].l = nd[0].r = 0; char cmd[3]; scanf("%s", cmd); if (cmd[0] == 'F') { int id = newNode(); int x = getFirst(rt); splay(x, 0); if (x) nd[x].l = id; nd[id].f = x; splay(id, 0); } else if (cmd[0] == 'B') { int x = getLast(rt); int id = newNode(); splay(x, 0); if (x) nd[x].r = id; nd[id].f = x; splay(id, 0); } else { int id; scanf("%d", &id); printf("%d\n", Gao(id)); } } } return 0; }
K题: Profact
1e5组数据,输入一个a(1<=a<=1e18),问a是否能用若干个阶乘的乘积得到。因为20的阶乘就已经超过了1e18,直接把所有的组合情况放进set,然后二分查找即可。
#include <cstdio> #include <cstring> #include <algorithm> #include <set> using namespace std; #define LL long long #define INF 1e18 LL num[20]; set<LL> Set; void solve(LL x, int s) { for (int i = s; i<20; i++) { if (INF/x < num[i]) return; Set.insert(x*num[i]); solve(x*num[i], i); } } void init() { num[1] = 1; for (int i = 2; i<20; i++) num[i] = num[i-1]*i; Set.insert(1); solve(1, 2); } int main() { init(); int t; scanf("%d", &t); while (t--) { LL x; scanf("%I64d", &x); if (Set.find(x) != Set.end()) puts("YES"); else puts("NO"); } return 0; }
L题: Emoticons
水模拟{% highlight c++ %} #include <cstdio> #include <cstring> using namespace std; const int maxn = 1e5+7; char str[maxn]; int main() { int n; while (scanf("%d", &n) != EOF) { scanf("%s", str); int sm = 0, sa = 0; for (int i = 0; i < n; i++) { if (str[i] == ':') { if (i - 1 >= 0) { if (str[i-1] == '(') sm++; if (str[i-1] == ')') sa++; } if (i + 1 < n) { if (str[i+1] == '(') sa++; if (str[i+1] == ')') sm++; } } } if (sm > sa) printf("HAPPY\n"); else if (sm < sa) printf("SAD\n"); else printf("BORED\n"); } return 0; }
相关文章推荐
- nyoj--528--找球号(三)(位运算&&set)
- [273]Integer to English Words
- Mysql千万级别数据优化方案(单表)
- undname.exe的使用
- HDU 5527(Too Rich-贪心)
- XE6发布文件 在Deployment Manager中添加待发布的文件,Remote Path写入assets\internal\或assets\就可以
- nyoj--528--找球号(三)(位运算&&set)
- 排序算法-插入排序_直接插入排序
- 自定义spring schema简化与canal集成
- 诚风老师-直销十三步走
- iOS.swift 如何设置tableview禁止上下滚动
- ReAct 常用 的属性
- Broadcast广播的使用
- TimesTen 模拟锁表操作
- 正则pumping lemma和infeasible path
- sicily 1237. Paint Mix
- solr facet查询及solrj 读取facet数据【facet.prefix应用:搜索引擎拼写提示(也可用suggest);Solr 默认 requestHandler已包含 Facet 组件】
- 算数-指数和根
- 2012 Asia Tianjin Regional Contest - Sum of divisors 暴力
- oracle rac查看磁盘组使用情况