[题解]NOIP2014提高组の题解集合

来源:互联网 发布:软件迭代记录 编辑:程序博客网 时间:2024/06/12 00:01

Day 1

T1 rps

模拟题。根据(i1)modNA+1(i1)modNB+1的值判断这一次的胜负来统计答案。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}const int N = 205;int ac[5][5] = {    {0, -1, 1, 1, -1},    {1, 0, -1, 1, -1},    {-1, 1, 0, -1, 1},    {-1, -1, 1, 0, 1},    {1, 1, -1, -1, 0}}, n, na, nb, cnt1, cnt2, a[N], b[N];int main() {    //freopen("rps.in", "r", stdin);    //freopen("rps.out", "w", stdout);    int i; n = read(); na = read(); nb = read();    for (i = 1; i <= na; i++) a[i] = read();    for (i = 1; i <= nb; i++) b[i] = read();    for (i = 1; i <= n; i++) {        int x = a[(i - 1) % na + 1], y = b[(i - 1) % nb + 1];        if (ac[x][y] == 1) cnt1++;        else if (ac[x][y] == -1) cnt2++;    }    printf("%d %d\n", cnt1, cnt2);    return 0;}

考虑到uv距离为2的两种可能:
1、uv的爷爷或vu的爷爷。
2、uv的父亲相同。
对于情况1,由于一个节点最多只有一个爷爷,所以情况1简单统计(由于是有序点对,所以要乘2)。
对于情况2,先枚举uv的父亲fa
对于寻找最大值,找出fa的权值最大的两个子节点,用它们权值的积更新答案。
对于求和,先不考虑是否选了两个相同的子节点。
sumfa的子节点权值和,那么如果固定一个子节点u,那么所有的子节点都可以充当v,所以此时对答案的贡献为Wusum。这样总和就为sum2
然后从sum2中去除掉选了同一个子节点的情况,就可以计入答案了。
注意:1、最大值不要取模。2、合法点对(u,v)(v,u)是两个不同的点对。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}typedef long long ll;const int N = 2e5 + 5, PYZ = 10007;int n, tot, a[N], w[N], ecnt, nxt[N << 1], adj[N], go[N << 1], sum;ll mv;void add_edge(int u, int v) {    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;}bool comp(int x, int y) {    return w[x] < w[y];}void dfs(int u, int fu) {    int i; tot = 0; ll res = 0, tmp = 0;    for (int e = adj[u], v; e; e = nxt[e])        if ((v = go[e]) != fu) a[++tot] = v, res += w[v],            tmp += 1ll * w[v] * w[v];    sort(a + 1, a + tot + 1, comp); ll tm = sum;    (sum += ((res % PYZ) * (res % PYZ) - tmp % PYZ) % PYZ + PYZ) %= PYZ;    if (tot >= 2) mv = max(mv, 1ll * w[a[tot - 1]] * w[a[tot]]);    if (fu) {        mv = max(mv, 1ll * w[fu] * w[a[tot]]);        (sum += 2ll * w[fu] * res % PYZ) %= PYZ;    }    for (int e = adj[u], v; e; e = nxt[e])        if ((v = go[e]) != fu) dfs(v, u);}int main() {    //freopen("link.in", "r", stdin);    //freopen("link.out", "w", stdout);    int i, x, y; n = read();    for (i = 1; i < n; i++) x = read(), y = read(), add_edge(x, y);    for (i = 1; i <= n; i++) w[i] = read();    dfs(1, 0); cout << mv << " " << sum << endl;    return 0;}

T3 bird

完全背包DP。设f[i][j]为到了位置(i,j)的最少点击次数,如果无法到达则为。一开始先把f的所有数初始化为(以下把所有的f[i][0]都规定为)。
边界为:
0<jm,f[0][j]=0
转移:
1、当j=m时,
0k<m,f[i][j]=min(f[i][j],f[i1][k]+jk1Xi1+1)
f[i][j]=min(f[i][j],f[i1][m]+1)
(即当高度达到m时继续操作则保持高度)
2、当jXi1时,
如果jXi12,则
f[i][j]=min(f[i][j],min(f[i1][jXi1]+1,f[i][jXi1]+1))
否则f[i][j]=min(f[i][j],f[i1][jXi1]+1)
(即此操作上升)
3、当jmYi1时,
f[i][j]=min(f[i][j],f[i1][j+Yi1])
(即此操作下降)
注意一些细节。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}const int N = 1e4 + 5, M = 1005, INF = 0x3f3f3f3f;int n, m, K, X[N], Y[N], U[N], D[N], f[N][M];bool is[N];void chkmin(int &a, int b) {a = min(a, b);}int main() {    //freopen("bird.in", "r", stdin);    //freopen("bird.out", "w", stdout);    int i, j, k, x, cnt = 0; n = read(); m = read(); K = read(); U[n] = m + 1;    for (i = 0; i < n; i++) X[i] = read(), Y[i] = read(), U[i] = m + 1;    for (i = 1; i <= K; i++) x = read(), D[x] = read(), U[x] = read(),        is[x] = 1;    for (i = 1; i <= n; i++) for (j = 0; j <= m + 1; j++) f[i][j] = INF;    for (i = 1; i <= n; i++) {        for (j = 0; j <= m; j++) {            if (j == m) {                for (k = 0; k <= m; k++)                    chkmin(f[i][j], f[i - 1][k] + (                        k == m ? 1 : (j - k - 1) / X[i - 1] + 1));                continue;            }            if (j >= X[i - 1]) {                if (j >= (X[i - 1] << 1)) chkmin(f[i][j],                    min(f[i - 1][j - X[i - 1]] + 1, f[i][j - X[i - 1]] + 1));                else chkmin(f[i][j], f[i - 1][j - X[i - 1]] + 1);            }        }        for (j = 0; j <= m - Y[i - 1]; j++)            chkmin(f[i][j], f[i - 1][j + Y[i - 1]]);        for (j = 0; j <= D[i]; j++) f[i][j] = INF;        for (j = U[i]; j <= m + 1; j++) f[i][j] = INF;        if (is[i]) {            bool flag = 0;            for (j = D[i] + 1; j <= U[i] - 1; j++)                if (f[i][j] < INF) {flag = 1; break;}            if (flag) cnt++;        }    }    int res = INF;    for (j = 1; j <= m; j++) chkmin(res, f[n][j]);    if (res < INF) printf("1\n%d\n", res);    else printf("0\n%d\n", cnt);    return 0;}

Day 2

T1 wireless

模拟题,暴力枚举每个区域即可。注意边界。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}const int N = 134;int d, n, a[N][N];int check(int x, int y) {    int i, j, x1, y1, x2, y2, ans = 0;    x1 = max(0, x - d); y1 = max(0, y - d);    x2 = min(128, x + d); y2 = min(128, y + d);    for (i = x1; i <= x2; i++) for (j = y1; j <= y2; j++)        ans += a[i][j]; return ans;}int main() {    //freopen("wireless.in", "r", stdin);    //freopen("wireless.out", "w", stdout);    int i, j, tot = 0, ans = 0; d = read(); n = read();    for (i = 1; i <= n; i++) a[read()][read()] = read();    for (i = 0; i <= 128; i++) for (j = 0; j <= 128; j++)        ans = max(ans, check(i, j));    for (i = 0; i <= 128; i++) for (j = 0; j <= 128; j++)        tot += (check(i, j) == ans);    printf("%d %d\n", tot, ans);    return 0;}

T2 road

构造出一个原图和一个各边方向相反的反图。先在反图上从终点开始进行一遍DFS,标记出可以到达终点的点,再标记出路径上不能包含(即存在一个与其相邻的节点不能到达终点)的节点,然后SPFA即可。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}const int N = 1e4 + 5, M = 2e5 + 5, INF = 0x3f3f3f3f;int n, m, ecnt, nxt[M], adj[N], go[M], ecnt2, nxt2[M], adj2[N], go2[M],que[M << 2], len, dis[N], S, T; bool vis[N], con[N], can[N];void add_edge(int u, int v) {    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;    nxt2[++ecnt2] = adj2[v]; adj2[v] = ecnt2; go2[ecnt2] = u;}void dfs(int u) {    can[u] = 1;    for (int e = adj2[u], v; e; e = nxt2[e])        if (!can[v = go2[e]]) dfs(v);}void mark() {    memset(con, true, sizeof(con));    int i; for (i = 1; i <= n; i++) for (int e = adj[i]; e; e = nxt[e])        if (!can[go[e]]) {con[i] = 0; break;}}int spfa() {    if (!con[S]) return -1;    int i; memset(dis, INF, sizeof(dis));    dis[que[len = 1] = S] = 0;    for (i = 1; i <= len; i++) {        int u = que[i]; vis[u] = 0;        for (int e = adj[u], v; e; e = nxt[e])            if (con[v = go[e]] && dis[u] + 1 < dis[v]) {                dis[v] = dis[u] + 1;                if (!vis[v]) vis[que[++len] = v] = 1;            }    }    return dis[T] < INF ? dis[T] : -1;}int main() {    //freopen("road.in", "r", stdin);    //freopen("road.out", "w", stdout);    int i, x, y; n = read(); m = read();    for (i = 1; i <= m; i++) x = read(), y = read(),        add_edge(x, y); S = read(); T = read();    printf("%d\n", (dfs(T), mark(), spfa()));    return 0;}

T3 equation

首先,考虑怎样解决高精度计算的复杂度问题。
可以想到取模。选pp为常数,约在510之间)个质数(约在104105之间),对每个系数分别取模,对于判断一个解是否合法,即判断在这p个模数的意义下,原式的值是否都为0。这样就得到了O(nmp)的算法。
考虑继续优化。可以发现,对于任意整数a,x,在模b意义下,自变量为xab+x时原式的值是同余的。所以只要对于每一个模数bi,预处理出自变量从0bi1时原式的值。这样枚举解时,只要求出了该自变量取模每个质数的结果,就可以得出解了。
复杂度O(nb+mp)b为选取的质数之和。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}const int N = 105, M = 1e4 + 5, MOD = 5e4 + 5;int PYZ[] = {10007, 25933, 27367, 38501, 41177}, n, m, val[N][7],sol[7][MOD];char s[M];int ModPYZ(int len, int pyz) {    int i = 1 + (s[1] == '-'), ans = 0;    for (; i <= len; i++) ans = (ans * 10 + s[i] - '0') % pyz;    if (s[1] == '-') ans = pyz - ans; if (ans == pyz) ans = 0;    return ans;}int solve(int x, int id) {    int i, now = 1, ans = 0;    for (i = 0; i <= n; i++, (now *= x) %= PYZ[id])        (ans += val[i][id] * now) %= PYZ[id];    return ans;}int main() {    //freopen("equation.in", "r", stdin);    //freopen("equation.out", "w", stdout);    int i, j, l, cnt = 0; n = read(); m = read();    for (i = 0; i <= n; i++) {        scanf("%s", s + 1); l = strlen(s + 1);        for (j = 0; j < 5; j++) val[i][j] = ModPYZ(l, PYZ[j]);    }    for (i = 0; i < 5; i++) for (j = 0; j < PYZ[i]; j++)        sol[i][j] = solve(j, i);    for (i = 1; i <= m; i++) {        bool flag = 1; for (j = 0; j < 5; j++)            flag = flag && (sol[j][i % PYZ[j]] == 0);        if (flag) cnt++;    }    printf("%d\n", cnt);    for (i = 1; i <= m; i++) {        bool flag = 1; for (j = 0; j < 5; j++)            flag = flag && (sol[j][i % PYZ[j]] == 0);        if (flag) printf("%d\n", i);    }    return 0;}
原创粉丝点击