10.3 NOIP模拟赛 DP + 最小生成树 + 容斥

来源:互联网 发布:手机三维绘图软件 编辑:程序博客网 时间:2024/05/22 13:14

Mine

这里写图片描述
考虑dp[i][0/1][0/1], 表示第i位当前有雷(1), 无雷(0), 以及下一位有雷或者无雷. 转移见代码注释.

#include<stdio.h>#include<cstring>using namespace std;const int mod = 1e9 + 7;const int maxn = 1000005;char s[maxn];int f[maxn][2][2], ans;int main(){    freopen("mine.in", "r", stdin);    freopen("mine.out", "w", stdout);    scanf("%s", s);    int len = strlen(s);    if(s[0] == '2') {puts("0"); return 0;}    if(s[0] == '0') f[0][0][0] = 1;    if(s[0] == '1') f[0][0][1] = 1;    if(s[0] == '*') f[0][1][0] = f[0][1][1] = 1;    if(s[0] == '?') f[0][0][0] = f[0][0][1] = f[0][1][0] = f[0][1][1] = 1;    for(int i = 1; i < len; ++i){        if(s[i] == '0') f[i][0][0] = f[i - 1][0][0]; //旁边都不能有雷         if(s[i] == '1'){            f[i][0][1] = f[i - 1][0][0]; //雷在右边             f[i][0][0] = f[i - 1][1][0]; //雷在左边         }        if(s[i] == '2') f[i][0][1] = f[i - 1][1][0]; //两边都有, 只有一种转移         if(s[i] == '*') f[i][1][0] = f[i][1][1] = (f[i - 1][0][1] + f[i - 1][1][1]) % mod;        if(s[i] == '?'){            f[i][0][0] = f[i][0][1] = (f[i - 1][1][0] + f[i - 1][0][0]) % mod;            f[i][1][0] = f[i][1][1] = (f[i - 1][1][1] + f[i - 1][0][1]) % mod;        }    }    ans = (f[len - 1][0][0] + f[len - 1][1][0]) % mod;    printf("%d\n", ans);}

Water

这里写图片描述

这道题简直神题…好吧是我最小生成树的性质了解的太少了. 这道题说是在下雨但是相当于就是矩形外面有无限的水量涌进来, 最后不再进水后整个矩形最终的形态就是答案.
一开始以为对于某个点的答案就取四个方向的min值与自己的差就可以了, 后来才发现明明这个点可以存在于一个盆地的中央, 盆地边缘不一定在四周. 这个点还可以存在于高原的盆地, 盆地的盆地, 地势多样…
但是不难发现, 对于某一个点的答案, 因为短板效应, 相当于就是矩形外面的水涌进来, 答案就是min(任意一条到这个点的路径的最大值) -这个点的高度(当然若为负就为0). 其实最大边权的最小值的路一定就叫作最小瓶颈路. 这个路一定在最小生成树上. 那么我们加入’矩形外’这个点, 每个点之间向四周建边(边权为两个点之间的较大值). 边界点就会与矩形外这个点连边. 做一遍最小生成树, 然后从矩形外这个点开始dfs,就求到了矩形外到每个点的最小瓶颈路.

最小瓶颈路在最小生成树其实很好证. 因为假设u, v的最小瓶颈路的最大值不在最小生成树上的话, 那么这个边权一定比最小生成树上的u, v之间的路径要小. 因为这个边不在最小生成树上所以连在这上面就一定成环, 因为之前说过小于最小生成树上的最大值要小, 现在又构成了环, 所以这个最小瓶颈路的最大值去替换它. 与最小生成树的定义相悖. 所以一定在最小生成树上. 反正可得.

#include<stdio.h>#include<algorithm>using namespace std;const int maxn = 400005;int h[maxn], a[maxn], fa[maxn], mx[maxn], tot, n, m, num;struct edge{int u, nxt, v, w;}e[maxn], c[maxn];int find(int x) {return (fa[x] == x) ? x : fa[x] = find(fa[x]);}inline void add(int u, int v, int w){    e[++num].v = v, e[num].w = w, e[num].nxt = h[u], h[u] = num;    e[++num].v = u, e[num].w = w, e[num].nxt = h[v], h[v] = num;}inline void adde(int u, int v, int w){    c[++num].u = u, c[num].v = v, c[num].w = w;}inline bool cmp(edge x, edge y){    return x.w < y.w;}inline void Kruskal(){    sort(c + 1, c + num + 1, cmp);    for(int i = 0; i <= n * m; ++i) fa[i] = i;    for(int i = 1; i <= num; ++i){        int x = find(c[i].u), y = find(c[i].v);        if(x != y){            ++tot; fa[x] = y;            add(c[i].u, c[i].v, c[i].w);            if(tot == n * m) break;        }    }    num = 0;}void dfs(int u, int fa){    for(int i = h[u]; i; i = e[i].nxt){        int v = e[i].v;        if(v == fa) continue;        mx[v] = max(mx[u], e[i].w);        dfs(v, u);    }}int main(){    freopen("water.in", "r", stdin);    freopen("water.out", "w", stdout);    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; ++i)        for(int j = 1; j <= m; ++j)            scanf("%d", &a[(i - 1) * m + j]);    for(int i = 1; i <= n; ++i)        for(int j = 1; j <= m; ++j){            int now = (i - 1) * m + j;            int fir = (j == 1) ? 0 : (i - 1) * m + j - 1;            int sec = (j == n) ? 0 : (i - 1) * m + j + 1;            int thi = (i == 1) ? 0 : (i - 2) * m + j;            int fou = (i == n) ? 0 : i * m + j;            adde(now, fir, max(a[now], a[fir])), adde(now, sec, max(a[now], a[sec]));            adde(now, thi, max(a[now], a[thi])), adde(now, fou, max(a[now], a[fou]));         }    Kruskal();    dfs(0, 0);    for(int i = 1; i <= n; ++i, puts(""))        for(int j = 1; j <= m; ++j)            printf("%d ", max(mx[(i - 1) * m + j] - a[(i - 1) * m + j], 0));    return 0;}/*3 34 4 02 1 33 3 -1*/

Gcd

给出n个数, 一开始都没被选.每次改变一个数的状态, 问当前所有数互质的无序对有多少对.
n <= 200000
直接上容斥原理. 比如当前加入x, 那么与x互质说明与x的质因数也互质. 将x质因数分解后, 设得到2, 3, 5, 7, 与x互质的数根据容斥原理可得就是 所有数 - 与2不互质的数(2的倍数)……+与2 * 3不互质的数……. 系数就是莫比乌斯函数.
将每个数因子分解就能维护上述 与2不互质的数…等.
o( n根号n)

#include<stdio.h>typedef long long dnt;const int maxm = 500005;bool mark[maxm]; dnt ans;int mu[maxm], pr[maxm], cnt[maxm], tot;inline const int read(){    register int x = 0;    register char ch = getchar();    while(ch < '0' || ch > '9') ch = getchar();    while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();    return x;}int a[maxm], c[maxm], n, m, num;inline void sieve(){    for(int i = 2; i <= maxm; ++i){        if(!mark[i]) pr[++tot] = i, mu[i] = 1;        for(int j = 1; j <= tot && pr[j] * i <= maxm; ++j){            mark[pr[j] * i] = true;            if((i % pr[j]) == 0){                mu[i * pr[j]] = 0;                 break;            }            else mu[i * pr[j]] = -mu[i];        }    }}inline dnt query(int x){    dnt ret = cnt[x] * mu[x];    for(int i = 2; i * i <= x; ++i)        if(!(x % i)){            if(i * i == x) ret += cnt[i] * mu[i];            else ret += cnt[i] * mu[i] + cnt[x / i] * mu[x / i];        }    return ret;}inline void modify(int x, int delta){    cnt[x] += delta; num += delta;    for(int i = 2; i * i <= x; ++i)        if(!(x % i)){            if(i * i == x) cnt[i] += delta;            else cnt[i] += delta, cnt[x / i] += delta;        }}int main(){    freopen("gcd.in", "r", stdin);    freopen("gcd.out", "w", stdout);    sieve();int x;    n = read(), m = read();    for(int i = 1; i <= n; ++i) a[i] = read();    for(int i = 1; i <= m; ++i){        x = read();        c[x] ^= 1;        if(c[x]) ans += num - query(a[x]), modify(a[x], +1);        else modify(a[x], -1), ans -= num - query(a[x]);        printf("%I64d\n", ans);    }}

这两天考试状态不是很好, 发现主要问题是想的时间太多, 总是不敢动手. 稍微有一点没想对就始终不动手. 这就导致虽然代码能力还不错, 但是给的时间很少, 有的时候没想出来一道题就耽误很多时间, 写暴力也很仓促. 所以决定以后一道题20分钟内想不出来必须写暴力(如果会写的话), 不能太纠结. 刚开始看题的时间缩减为10min.

还有对于普通算法, 还不够精. 要少王偏难怪钻, 多多挖掘基础算法的性质, 多做模型转化的题目.