10.5 NOIP模拟赛 割点+贪心+最小点覆盖

今天的题很简单, 估分200+但是每个程序都写挂了2个字符左右, 居然爆零…估分这么高简直让我无地自容…退役吧退役吧. 好在写的是正确的考完试改一下就300了. 有的时候从0到300只需要很短的时间…

Board

这里写图片描述

数据是300 * 300的.
首先有一个很显然的结论, 答案不是1就是2. 因为你可以在角落上抠两个土地出来, 这样角落上那个土地就与其他不连通了. 那么我们就只需要考虑 有没有只需要挖一个点就可以使整个图不连通的情况.那么相当于这个点就是割点. 所以建图后跑tarjan即可.
注意土地数<=2的时候删哪一个都不行, 输出-1.

Change

#include<stdio.h>
#include<cstring>
#include<algorithm>
#define clear(a) memset(a, 0, sizeof(a))
#define id(a, b) (a - 1) * m + b
using namespace std;
const int maxn = 200005;
int dx[6] = {0, 1, 0, 0, -1};
int dy[6] = {0, 0, -1, 1, 0};
bool iscut[maxn];
char ch[305][305];
int cnt, n, m, T, alos, num, indexx;
int h[maxn], pre[maxn], low[maxn];
struct edge{ int nxt, v;}e[maxn * 2];
inline void add(int u, int v) {e[++num].v = v, e[num].nxt = h[u], h[u] = num;}
int dfs(int u, int fa){
    int child = 0;
    int lowu = pre[u] = ++indexx;
    for(int i = h[u]; i; i = e[i].nxt){
        int v = e[i].v;
        if(!pre[v]){
            child ++;
            int lowv = dfs(v, u);
            lowu = min(lowu, lowv);
            if(lowv >= pre[u]) iscut[u] = true, cnt++;
        }
        else if(pre[v] < pre[u] && v != fa) lowu = min(lowu, pre[v]);
    }
    if(fa < 0 && child == 1) iscut[u] = false, --cnt;
    low[u] = lowu;
    return lowu;
}
int main(){
    freopen("board.in", "r", stdin);
    freopen("board.out", "w", stdout);
    scanf("%d", &T);
    while(T--){
        clear(iscut), clear(pre), clear(h), clear(low), num = alos = cnt = indexx = 0;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i)
            scanf("%s", ch[i] + 1);
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j)
                if(ch[i][j] == '#'){
                    alos++;
                    for(int k = 1; k <= 4; ++k)
                        if(i + dx[k] <= n && i + dx[k] >= 1 && j + dy[k] >= 1 && j + dy[k] <= m)
                            if(ch[i + dx[k]][j + dy[k]] == '#')
                                add(id(i, j), id(i + dx[k], j + dy[k]));
                    }
        low[e[1].v] = dfs(e[1].v, -1);
        if(alos <= 2) puts("-1");
        else if(cnt) puts("1");
        else puts("2");
    }
}

这里写图片描述

注意数据里面说了A, B都是那13个风石之一. 那么我们可以想, 因为是最坏情况, 那么给你零钱一定是从大往小给, 中途尽量大. 因为如果给你很碎很小的零钱, 那么由于给你的总和一样, 那么给小的零钱就能组合成尽量大的这种方案里面的每种钱. 那么尽量给你小的比起给你尽量大的钱来说, 你只赚不亏. 所以对你尽量坏来说, 那就是尽量给你大的. 那么你就希望每次找的零钱尽量种类多, 尽量碎一点, 那么买价值为1的话就能把你当前手里的钱尽量破成尽量多的零钱. 由于给你的是尽量大的, 那么你能凑成B的时候绝对是刚好有个风石是B. 那么每次把零钱里你贪心的选刚好比B大的风石去买东西, 还是买价值为1的, 这样尽量碎同时每次买1价值也最小.

还有一种推理方法.
首先说结论 : 如果a=1,10,100,1000并且b != 2a,那么答案是2,否则是1.
考虑为什么,先分析1,2,5,10这几个小的,如果a = 1,b >= 5,那么肯定需要至少2,否则只需要1(这个可以枚举方案证明)。
然后考虑大的的情况,举个例子,如果a = 10,b = 50,那么买了1之后,需要找零49,这个9肯定是用1,2,5凑出来的(这里的意思是,能拼出49,一定能拼出9,然后剩下的部分即使有1,2,5,他们也一定能拼出10),减掉他们之后相当于用10,20,去凑40,就回到了上面的情况。其他情况类似证明。

贪心做法

#include<stdio.h>
int v[15]={0, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000};
int s[100005];
int ans, top, A, B, T;
inline void dfs(int now){
    top = 0; ++ ans;
    for(int i = 13; i && now; --i){
        while(v[i] <= now) s[++top] = v[i], now -= v[i];
        if(s[top] == B) return;
    }
    for(int i = top; i; --i)
        if(s[i] > B){
            dfs(s[i] - 1);
            break;
         }
}
int main(){
    freopen("change.in", "r", stdin);
    freopen("change.out", "w", stdout);
    scanf("%d", &T);
    while(T--){
        ans = 0;
        scanf("%d%d", &A, &B);
        dfs(A - 1);
        printf("%d\n", ans);
    }
    return 0;
}

Fill

题意
是给你一些树上路径,问最少需要多少个树上的点,把所有路径覆盖掉(即每条路径上至少有1条选择的点)。
数据是1e5的, 3组组数据.
我们也是贪心,按照路径两个端点的lca的深度从深到浅排序,然后贪心的选(即如果这个路径包含了被选择的点,跳过这条路径,否则选择这条路径的lca)。
证明:如果我们lca深度最深的那条路径上选择的点不是其lca,我们不选那个点,选择lca肯定不劣。然后把lca覆盖的路径删掉,这样就变成了1个子问题,证明完毕。
考虑怎么实现,如果我们选了u,v这条路径,我们只需要把lca(u,v)对应的子树那个区间都+1,判断某个区间是否被某个点覆盖只需要看u和v是否有点对应位置为0(以上都是在dfs序上操作),因为只需要实现区间加,单点查询,
故用棵树状数组即可。

lca深度浅的链要过lca深度深的链的话, 必过深的链的lca, 这点画画图就知道. 所以我们选LCA一定是不劣的.

#include<stdio.h>
#include<cstring>
#include<algorithm>
#define clear(a) memset(a, 0, sizeof(a))
using namespace std;
const int maxn = 200005;
int ans, n, m, T, indexx, num;
int b[maxn], in[maxn], out[maxn], son[maxn], dep[maxn], siz[maxn], h[maxn], fa[maxn], top[maxn];
struct edge{ int v, nxt;}e[maxn * 2];
struct point{ int u, v, lca;}c[maxn];
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;
}
inline bool cmp(point x, point y){
    return dep[x.lca] > dep[y.lca];
}
inline void add(int u, int v){
    e[++num].v = v, e[num].nxt = h[u], h[u] = num;
    e[++num].v = u, e[num].nxt = h[v], h[v] = num;
}
void dfs1(int u){
    siz[u] = 1;
    for(int i = h[u]; i; i = e[i].nxt){
        int v = e[i].v;
        if(v == fa[u]) continue;
        fa[v] = u, dep[v] = dep[u] + 1;
        dfs1(v); siz[u] += siz[v];
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}
void dfs2(int u, int tp){
    top[u] = tp;
    in[u] = ++indexx;
    if(son[u]) dfs2(son[u], tp);
    for(int i = h[u]; i; i = e[i].nxt){
        int v = e[i].v;
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v, v); 
    }
    out[u] = indexx;
}
int query(int u, int v){
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u, v);
    return v;
}
inline void modify(int x, int delta){
    for(int i = x; i <= n; i += i & -i) b[i] += delta;
}
inline int query(int x){
    int tmp = 0;
    for(int i = x; i; i -= i & -i) tmp += b[i];
    return tmp;
}
int main(){
    freopen("fill.in", "r", stdin);
    freopen("fill.out", "w", stdout);
    T = read();
    while(T--){
        clear(h), clear(b), clear(son), clear(top);
        num = ans = indexx = 0;
        n = read(), m = read();
        int x, y;
        for(int i = 1; i < n; ++i) x = read(), y = read(), add(x, y);
        dep[1] = 1, dfs1(1), dfs2(1, 1);
        for(int i = 1; i <= m; ++i) c[i].u = read(), c[i].v = read(), c[i].lca = query(c[i].u,c[i].v);
        sort(c + 1, c + m + 1, cmp);
        for(int i = 1; i <= m; ++i){
            if(query(in[c[i].u]) || query(in[c[i].v])) continue;
            ans++;
            modify(in[c[i].lca], +1), modify(out[c[i].lca] + 1, -1);
        }
        printf("%d\n", ans);
    }
}

今天代码时间还是太少, 不要想得太细, 有大概正确思路就先写着.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值