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

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 3
4 4 0
2 1 3
3 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.

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值