2014 Benelux Algorithm Programming Contest (BAPC 14) 部分题解

来源:互联网 发布:淘宝优惠群公告怎么写 编辑:程序博客网 时间:2024/05/22 03:23

太弱了。只能做一半_(:3∠)_

B - Button Bashing (bfs)

题意

问调到目标值的最小步,不然就输出最接近的。

思路

一开始不知道怎么就想到DP去了,先DP出正的,然后DP负的,还一直以为这样是对的。

bfs搞搞。

代码

#include <stack>#include <cstdio>#include <list>#include <cassert>#include <set>#include <fstream>#include <iostream>#include <string>#include <vector>#include <queue>#include <functional>#include <cstring>#include <algorithm>#include <cctype>//#pragma comment(linker, "/STACK:102400000,102400000")#include <string>#include <map>#include <cmath>//#include <ext/pb_ds/assoc_container.hpp>//#include <ext/pb_ds/hash_policy.hpp>using namespace std;//using namespace __gnu_pbds;#define LL long long#define ULL unsigned long long#define SZ(x) (int)x.size()#define Lowbit(x) ((x) & (-x))#define MP(a, b) make_pair(a, b)#define MS(arr, num) memset(arr, num, sizeof(arr))#define PB push_back#define X first#define Y second#define ROP freopen("input.txt", "r", stdin);#define MID(a, b) (a + ((b - a) >> 1))#define LC rt << 1, l, mid#define RC rt << 1|1, mid + 1, r#define LRT rt << 1#define RRT rt << 1|1#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)const double PI = acos(-1.0);const int INF = 0x3f3f3f3f;const double eps = 1e-4;const int MAXN = 3e5+10;const int MOD = 10007;const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };const int seed = 131;int cases = 0;typedef pair<int, int> pii;queue<pii> Q;int vis[10000], tar;vector<int> arr;void bfs(){    Q.push({0, 0});    while (!Q.empty())    {        pii u = Q.front(); Q.pop();        for (int i = 0; i < SZ(arr); i++)        {            int v = u.X + arr[i];            if (v > 3600) v = 3600;            if (vis[v] != INF || v <= 0) continue;            vis[v] = u.Y+1;            Q.push({v, u.Y+1});        }    }    if (vis[tar] != INF) printf("%d 0\n", vis[tar]);    else for (int i = tar+1; i <= 3600; i++)        if (vis[i] != INF)        {            printf("%d %d\n", vis[i], i-tar);            return;        }}int main(){    //ROP;    //freopen("out.txt", "w", stdout);    int T;    scanf("%d", &T);    while (T--)    {        arr.clear();        MS(vis, INF);        vis[0] = 0;        int n;        scanf("%d%d", &n, &tar);        for (int i = 0; i < n; i++)        {            int tmp;            scanf("%d", &tmp);            arr.PB(tmp);        }        bfs();    }    return 0;}

E - Excellent Engineers (线段树)

题意

如果一个人rank都比另一个人高,就可以干掉另一个人。

问最后有多少人能存活。

思路

显然最后存活的数和输入顺序无关。

假设三个属性分别为x、y、z。

我们可以先按x排序。

线段树的区间表示的是y,线段树维护的是某个范围内的y对应z的最小值。

之后假设我们现在要插入第x个人。

显然他不能消灭掉前x-1个人,因为前面人至少x的rank比他高。所以我们考虑他能不能存活下来。

假设第x个人的y=y0, z=z0。

如果存在一个人,使得y<y0andz<z0,那么第x个人就活不下来。否则就可以。

所以我们可以在线段树的(1~y0-1)区间内查询它的最小值,如果最小值<z0,下一个。大于的话就把当前这个人插到树里。

代码

#include <stack>#include <cstdio>#include <list>#include <cassert>#include <set>#include <fstream>#include <iostream>#include <string>#include <vector>#include <queue>#include <functional>#include <cstring>#include <algorithm>#include <cctype>//#pragma comment(linker, "/STACK:102400000,102400000")#include <string>#include <map>#include <cmath>//#include <ext/pb_ds/assoc_container.hpp>//#include <ext/pb_ds/hash_policy.hpp>using namespace std;//using namespace __gnu_pbds;#define LL long long#define ULL unsigned long long#define SZ(x) (int)x.size()#define Lowbit(x) ((x) & (-x))#define MP(a, b) make_pair(a, b)#define MS(arr, num) memset(arr, num, sizeof(arr))#define PB push_back#define X first#define Y second#define ROP freopen("input.txt", "r", stdin);#define MID(a, b) (a + ((b - a) >> 1))#define LC rt << 1, l, mid#define RC rt << 1|1, mid + 1, r#define LRT rt << 1#define RRT rt << 1|1#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)const double PI = acos(-1.0);const int INF = 0x3f3f3f3f;const double eps = 1e-4;const int MAXN = 1e5+10;const int MOD = 10007;const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };const int seed = 131;int cases = 0;typedef pair<int, int> pii;struct POINT{    int x, y, z;    bool operator < (const POINT &a) const    {        return x < a.x;    }}p[MAXN];struct TREE{    int min_val;}t[MAXN<<2];void push_up(int rt){    t[rt].min_val = min(t[LRT].min_val, t[RRT].min_val);}void Update(int rt, int l, int r, int y, int z){    if (l == r)    {        t[rt].min_val = z;        return;    }    int mid = MID(l, r);    if (y <= mid) Update(LC, y, z);    else Update(RC, y, z);    push_up(rt);}int Query(int rt, int l, int r, int L, int R){    int ret = INF;    if (L <= l && r <= R) return t[rt].min_val;    int mid = MID(l, r);    if (L <= mid) ret = min(ret, Query(LC, L, R));    if (R >= mid+1) ret = min(ret, Query(RC, L, R));    return ret;}int main(){    //ROP;    int T;    scanf("%d", &T);    while (T--)    {        MS(t, INF);        int n;        scanf("%d", &n);        FOR(i, 0, n) scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].z);        sort(p, p+n);        int ans = 0;        for (int i = 0; i < n; i++)        {            if (p[i].y != 1)            {                int tmp = Query(1, 1, n, 1, p[i].y-1);                if (tmp < p[i].z) continue;            }            ans++;            Update(1, 1, n, p[i].y, p[i].z);        }        printf("%d\n", ans);    }    return 0;}

I - Interesting Integers (扩展欧几里得)

题意

可以定义Fibonacci数列的前两项,现在给出n,问满足题目要求的最小的两项。

思路

先仰慕illuz巨菊!

一开始老是想着分解n成两项之和,一直没思路。

然后请教了hcbbt巨巨。

我们知道,<=1e9的Finobacci数列只有50项。而且每一项的系数都是Fibonacci数列。

假设系数是x、y。

那么我们就是求ax+by = n的最小a、b。

然后就可以用扩展欧几里得搞了。

又加深了对扩展欧几里得的理解= =

代码

#include <stack>#include <cstdio>#include <list>#include <cassert>#include <set>#include <fstream>#include <iostream>#include <string>#include <vector>#include <queue>#include <functional>#include <cstring>#include <algorithm>#include <cctype>//#pragma comment(linker, "/STACK:102400000,102400000")#include <string>#include <map>#include <cmath>//#include <ext/pb_ds/assoc_container.hpp>//#include <ext/pb_ds/hash_policy.hpp>using namespace std;//using namespace __gnu_pbds;#define LL long long#define ULL unsigned long long#define SZ(x) (int)x.size()#define Lowbit(x) ((x) & (-x))#define MP(a, b) make_pair(a, b)#define MS(arr, num) memset(arr, num, sizeof(arr))#define PB push_back#define X first#define Y second#define ROP freopen("input.txt", "r", stdin);#define MID(a, b) (a + ((b - a) >> 1))#define LC rt << 1, l, mid#define RC rt << 1|1, mid + 1, r#define LRT rt << 1#define RRT rt << 1|1#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)const double PI = acos(-1.0);const int INF = 0x3f3f3f3f;const double eps = 1e-4;const int MAXN = 3e5+10;const int MOD = 10007;const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };const int seed = 131;int cases = 0;typedef pair<LL, LL> pii;void extend_gcd(LL a, LL b, LL &d, LL &x, LL &y){    if (!b) d = a, x = 1, y = 0;    else    {        extend_gcd(b, a%b, d, y, x);        y -= x * (a/b);    }}pii ans;void Solve(LL x, LL y, LL a, LL b){    LL k = -x / b;    x += k*b, y -= k*a;    if (x <= 0)    {        while (x <= 0) x += b, y -= a;        if (y < x) return;    }    else    {        while (x - b > 0) x -= b, y += a;        if (y < x) return;    }    k = (y-x-1) / (a+b);    x += b*k, y -= a*k;    while (y >= x && y <= ans.Y)    {        if (y < ans.Y || (y == ans.Y && x < ans.X)) ans.X = x, ans.Y = y;        x += b, y -= a;    }}int arr[200];int main(){    //ROP;    int T;    scanf("%d", &T);    arr[1] = arr[2] = 1;    for (int i = 3; i <= 50; i++) arr[i] = arr[i-1] + arr[i-2];    while (T--)    {        ans.X = INF, ans.Y = INF;        int n;        scanf("%d", &n);        for (int i = 2; i <= 43; i++)        {            int a = arr[i-1], b = arr[i];            if (n % __gcd(a, b) != 0) continue;            LL d, x, y;            extend_gcd(a, b, d, x, y);            a /= d, b /= d;            x *= n, y *= n;            Solve(x, y, a, b);        }        cout << ans.X << " " << ans.Y << endl;    }    return 0;}

J - Jury Jeopardy (模拟)

题意

让我们根据序列输出图。

思路

首先我们知道没有比起点更小的列。不然起点就会被封死。

模拟一下。先把所有的.给记上,然后记录一下行列的最大值和最小值,然后一排一排补全即可。

代码

#include <stack>#include <cstdio>#include <list>#include <cassert>#include <set>#include <fstream>#include <iostream>#include <string>#include <vector>#include <queue>#include <functional>#include <cstring>#include <algorithm>#include <cctype>//#pragma comment(linker, "/STACK:102400000,102400000")#include <string>#include <map>#include <cmath>//#include <ext/pb_ds/assoc_container.hpp>//#include <ext/pb_ds/hash_policy.hpp>using namespace std;//using namespace __gnu_pbds;#define LL long long#define ULL unsigned long long#define SZ(x) (int)x.size()#define Lowbit(x) ((x) & (-x))#define MP(a, b) make_pair(a, b)#define MS(arr, num) memset(arr, num, sizeof(arr))#define PB push_back#define X first#define Y second#define ROP freopen("input.txt", "r", stdin);#define MID(a, b) (a + ((b - a) >> 1))#define LC rt << 1, l, mid#define RC rt << 1|1, mid + 1, r#define LRT rt << 1#define RRT rt << 1|1#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)const double PI = acos(-1.0);const int INF = 0x3f3f3f3f;const double eps = 1e-4;const int MAXN = 300+10;const int MOD = 10007;//const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };const int seed = 131;int cases = 0;typedef pair<int, int> pii;struct DIRECTION{    pii f, b, l, r;    int db, dl, dr;}dir[4];char mp[500][500];void init(){    //0上1下2左3右    dir[0].f = {-1, 0}, dir[0].b = {1, 0}, dir[0].l = {0, -1}, dir[0].r = {0, 1};    dir[1].f = {1, 0}, dir[1].b = {-1, 0}, dir[1].l = {0, 1}, dir[1].r = {0, -1};    dir[2].f = {0, -1}, dir[2].b = {0, 1}, dir[2].l = {1, 0}, dir[2].r = {-1, 0};    dir[3].f = {0, 1}, dir[3].b = {0, -1}, dir[3].l = {-1, 0}, dir[3].r = {1, 0};    dir[0].db = 1, dir[0].dl = 2, dir[0].dr = 3;    dir[1].db = 0, dir[1].dl = 3, dir[1].dr = 2;    dir[2].db = 3, dir[2].dl = 1, dir[2].dr = 0;    dir[3].db = 2, dir[3].dl = 0, dir[3].dr = 1;}string str;void Solve(){    int x = 250, y = 250, cur = 3, ans = 0;    int max_col = -1, max_row = -1, min_row = INF;    for (int i = 0; i < SZ(str); i++)    {        max_col = max(max_col, y);        max_row = max(max_row, x);        min_row = min(min_row, x);        assert(y >= 250);        char c = str[i];        if (c == 'F')        {            mp[x+dir[cur].f.X][y+dir[cur].f.Y] = '.';            x += dir[cur].f.X, y += dir[cur].f.Y;        }        else if (c == 'B')        {            x += dir[cur].b.X, y += dir[cur].b.Y;            mp[x][y] = '.';            cur = dir[cur].db;        }        else if (c == 'L')        {            x += dir[cur].l.X, y += dir[cur].l.Y;            mp[x][y] = '.';            cur = dir[cur].dl;        }        else        {            x += dir[cur].r.X, y += dir[cur].r.Y;            mp[x][y] = '.';            cur = dir[cur].dr;        }    }    for (int i = 250; i <= max_col+1; i++)        for (int j = min_row-1; j <= max_row+1; j++)            mp[j][i] = (mp[j][i] == '.' ? '.' : '#');    printf("%d %d\n", max_row-min_row+3, max_col+2-250);    for (int i = min_row-1; i <= max_row+1; i++) printf("%s\n", mp[i]+250);}int main(){    //ROP;    init();    int T;    scanf("%d", &T);    printf("%d\n", T);    while (T--)    {        MS(mp, 0);        cin >> str;        Solve();    }    return 0;}

K - Key to Knowledge (中途相遇法 + 状态压缩)

题意

给出n个01串,每个01串后面有个数字,代表正确的个数。问正确的答案有几种。如果只有一种输出。

思路

首先想到状态压缩,但是(1<<30)的规模显然要跪。

然后又想了一会儿中途相遇,想了一会儿,想不出相遇什么东西。

然后又YY了一些解法,还是想不出来。
然后就请教hcbbt巨巨。

然后他想到了枚举少的一方的状态然后暴力。我一听也觉得可以。然而我们都以为那时候的状态是(1<<k)了。

直到我敲完代码调试的时候才发现,那样状态是很多的( TДT)

然后就看了题解。

正解是用中途相遇法。

我们可以通过枚举前m/2位的正确答案的个数,然后得到这种情况下各串后面剩下的位需要多少个正确的答案,存在map里。

然后我们枚举剩下部分的正确答案个数。在map里找是不是有一模一样的。如果是的话,说明这部分的正确答案和以前那部分的正确答案是可以拼起来的,也就是完整的答案。

本来这个过程是O(nlogn)的,但是因为n比较小,可以直接按进制hash成一个数字,这样就变成了O(logn)。

学习了红名爷玄幻的代码。。。

代码

#include <stack>#include <cstdio>#include <list>#include <cassert>#include <set>#include <fstream>#include <iostream>#include <string>#include <vector>#include <queue>#include <functional>#include <cstring>#include <algorithm>#include <cctype>//#pragma comment(linker, "/STACK:102400000,102400000")#include <string>#include <map>#include <cmath>//#include <ext/pb_ds/assoc_container.hpp>//#include <ext/pb_ds/hash_policy.hpp>using namespace std;//using namespace __gnu_pbds;#define LL long long#define ULL unsigned long long#define SZ(x) (int)x.size()#define Lowbit(x) ((x) & (-x))#define MP(a, b) make_pair(a, b)#define MS(arr, num) memset(arr, num, sizeof(arr))#define PB push_back#define X first#define Y second#define ROP freopen("input.txt", "r", stdin);#define MID(a, b) (a + ((b - a) >> 1))#define LC rt << 1, l, mid#define RC rt << 1|1, mid + 1, r#define LRT rt << 1#define RRT rt << 1|1#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)const double PI = acos(-1.0);const int INF = 0x3f3f3f3f;const double eps = 1e-4;const int MAXN = 3e5+10;const int MOD = 10007;const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };const int seed = 131;int cases = 0;typedef pair<int, int> pii;struct POINT{    string str;    int ans;}p[20];map<LL, int> mp;LL hsh[1<<16];int m, n;void Solve(){    mp.clear();    for (int sta = 0; sta < (1<<m/2); sta++)    {        LL x = 0;        bool ok = true;        for (int i = 0; i < n; i++)        {            int cur_ans = p[i].ans;            for (int j = 0; j < m/2; j++)                if ((p[i].str[j] == '1') == ((sta>>j)&1)) //如果出现了一个正确答案                    cur_ans--;            if (cur_ans < 0) ok = false;            x = x * (m+1) + cur_ans;        }        if (!ok) x = -1;        hsh[sta] = x;        mp[x]++;    }    int ans_num = 0, ans = -1;    for (int sta = 0; sta < (1<<(m-m/2)); sta++)    {        LL x = 0;        for (int i = 0; i < n; i++)        {            int cur_ans = 0;            for (int j = m/2; j < m; j++)                if ((p[i].str[j] == '1') == ((sta>>(j-m/2)&1))) cur_ans++;            x = x * (m+1) + cur_ans;        }        ans_num += mp[x];        if (ans == -1 && mp[x] > 0)        {            for (int i = 0; i < (1<<(m/2)); i++)                if (x == hsh[i])                    ans = i + (sta<<(m/2));        }    }    if (ans_num != 1) printf("%d solutions\n", ans_num);    else    {        for (int i = 0; i < m; i++)            printf("%d", (ans>>i)&1);        puts("");    }}int main(){    //ROP;    int T;    scanf("%d", &T);    while (T--)    {        scanf("%d%d", &n, &m);        for (int i = 0; i < n; i++) cin >> p[i].str >> p[i].ans;        Solve();    }    return 0;}
0 0