高二&高一&初三模拟赛24 总结

前言

国庆假时的比赛大多已经记不清了,记得这次比赛第一题想复杂了,然后发挥就失常了,看来第一题不要想太多啊。!!。


Karen与游戏

题目描述

这里写图片描述
这里写图片描述


输入格式

这里写图片描述


输出格式

这里写图片描述


输入样例

输入样例1

3 5
2 2 2 3 2
0 0 0 1 0
1 1 1 2 1

输入样例2

3 3
0 0 0
0 1 0
0 0 0

输入样例3

3 3
1 1 1
1 1 1
1 1 1


输出样例

输出样例1

4
row 1
row 1
col 4
row 3

输出样例2

-1

输出样例3

3
row 1
row 2
row 3


题解(贪心)

这题我想复杂了,一开始当成最小割,于是码完了发现图错了,改了半天无果就搁下了。其实这就是个明显的贪心,假如一行要消一次肯定消最小的那个,然后列也一样。不过如果行多先消列,列多先消行。

看来我的最小割贪心简直弱爆了。


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#define maxn 105
#define oo 0x7fffffff

using namespace std;

int n, m, ans, g[maxn][maxn];
int row[maxn], col[maxn];

void DelR(){
    for(int i = 1; i <= n; i++){
        int minv = oo;
        for(int j = 1; j <= m; j++)
            minv = min(minv, g[i][j]);
        for(int j = 1; j <= m; j++)
            g[i][j] -= minv;
        row[i] += minv;
    }
}

void DelC(){
    for(int j = 1; j <= m; j++){
        int minv = oo;
        for(int i = 1; i <= n; i++)
            minv = min(minv, g[i][j]);
        for(int i = 1; i <= n; i++)
            g[i][j] -= minv;
        col[j] += minv;
    }
}

int main(){

    scanf("%d%d", &n, &m);

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            scanf("%d", &g[i][j]);

    if(n <= m){  
        DelR();
        DelC();
    }

    else{
        DelC();
        DelR();
    }

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++){
            if(g[i][j]){
                puts("-1");
                return 0;
            }
        }    

    for(int i = 1; i <= n; i++)  ans += row[i];
    for(int i = 1; i <= m; i++)  ans += col[i];

    printf("%d\n", ans);

    for(int i = 1; i <= n; i++){
        while(row[i]){
            printf("row %d\n", i);
            row[i] --;
        }
    }

    for(int i = 1; i <= m; i++){
        while(col[i]){
            printf("col %d\n", i);
            col[i] --;
        }
    }

    return 0;
}

puzzle

题目描述

这里写图片描述
这里写图片描述


输入格式

这里写图片描述


输出格式

这里写图片描述


输入样例

输入样例1

7
1 2 1 1 4 4

输入样例2

12
1 1 2 2 4 4 3 3 1 10 8


输出样例

输出样例1

1.0 4.0 5.0 3.5 4.5 5.0 5.0

输出样例2

1.0 5.0 5.5 6.5 7.5 8.0 8.0 7.0 7.5 6.5 7.5 8.0


题解(树形DP+期望)

发现期望真的超级重要的!去年的noip考了期望,今年可能也在上面动刀。

这题我其实是找规律的,找到规律后就简单证明了一下发现是对的。题目求期望的dfs序,假如确定了根的dfs序期望为Exp[x],明显儿子的dfs序跟各个儿子的子树大小有关。何时搜到儿子v取决v被x访问的顺序,这个顺序是随机的,我们有一个序列,就是看看v在序列的哪个位置,期望的Exp[v]就是它前面那些兄弟的子树大小和+1+Exp[x]。我们期望一下它前面的子树大小和。

根据期望是均值的理论,想想觉得v应期望出现在序列的中间,就是说它前面的节点应是总节点的一半。于是Exp[v] = Exp[x] + (f[x] - f[v] - 1) / 2.0。其中f[x]代表x子树大小。于是这样就是对的。

另外的理解就是相当于选一个点v,将其撒在这个序列里,学过各种概型就很容易知道其他的点在他前后的概率是一样的,就是对于u,v随机撒,明显u在v前面的概率是1/2(类比几何概型,虽然这是古典。。)。然后每个点能贡献的就是1/2,乘上值就是期望了。这样更加好懂为什么是 (f[x] - f[v] - 1) / 2.0了吧。

严谨的证明也是有的,不过考试时一些题的证明是比较困难的,这是就要大胆去试了,慢慢看吧。

这里写图片描述


代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#define maxn 100010

using namespace std;

int n, cur = -1;
int head_p[maxn], f[maxn];
double Exp[maxn];
struct Adj{int obj, next;} Edg[maxn];

void Insert(int a, int b){
    cur ++;
    Edg[cur].next = head_p[a];
    Edg[cur].obj = b;
    head_p[a] = cur;
}

void dfs1(int x){
    f[x] = 1;
    for(int i = head_p[x]; ~ i; i = Edg[i].next){
        int v = Edg[i].obj;
        dfs1(v);
        f[x] += f[v];
    }
}

void dfs2(int x){
    for(int i = head_p[x]; ~ i; i = Edg[i].next){
        int v = Edg[i].obj;
        Exp[v] = 1 + Exp[x] + (f[x] - f[v] - 1) / 2.0;
        dfs2(v);
    }
}

int main(){

    scanf("%d", &n);

    for(int i = 1; i <= n; i++)  head_p[i] = -1;

    for(int i = 1, f; i < n; i++){
        scanf("%d", &f); 
        Insert(f, i+1);
    }


    Exp[1] = 1.0;

    dfs1(1);

    dfs2(1);

    for(int i = 1; i <= n; i++)
        printf("%.1lf ", Exp[i]);

    printf("\n");
    return 0;
} 

Karen与测试

这里写图片描述
这里写图片描述


题解(找规律+组合数学+逆元)

这题是最难的,因为它需要找规律。像我这种数学不好,编程找规律能力又差,高斯消元什么的也不会的就只能水分了。

赛后这题的题解看到我要呕血,十分丧病。后来我自己换了种方法,看看样例一发现一个神奇的东西,这个东西不是判断+-的搞来搞去烦到爆炸,而直接可以用优美的组合数学搞定一切。

36=31+92+151

而1 2 1就是杨辉三角,我们由此发现当n%4==1时,系数竟然是杨辉三角!!例如等于9时是1 4 6 4 1(死算一遍),就是说偶数项的那些被算来算去消掉了,于是我们就可以直接用 O(nlog(1e7)) 的方法搞,一边求组合数,然后费马小定理求逆元。一开始强制做到n%4==1就行了。

正经的说法就是第 i2+1 个数被取的次数是 Cin2 ,而贡献都是正的。由于底数一定,于是O(n)递推+逆元搞定。不过i=0的时候逆元会搞到C变成0,我这里特殊跳过了。

这种找规律的题还是要用程序多试几组数据,然后就找到规律了。(这种题就是试探题,数列求和真是够数学的)


代码

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 200010
#define MOD 1000000007

using namespace std;

typedef long long LL;
int n, m;
int a[maxn];
LL ans;
bool sign = true;

LL Pow(int x, int y){
    LL res = 1LL;
    while(y){
        if(y & 1)  res = 1LL * res * x % MOD;
        x = 1LL * x * x % MOD;
        y >>= 1;
    }
    return res;
}

int main(){

    scanf("%d", &n);

    for(int i = 1; i <= n; i++)  scanf("%d", &a[i]);

    while(n % 4 != 1){
        for(int i = 1; i < n; i++){
            if(sign)  a[i] = (a[i] + a[i+1]) % MOD;
            else  a[i] = (a[i] - a[i+1] + MOD) % MOD;
            sign ^= 1;
        }
        n --;
    }

    LL C = 1LL;
    int p = n >> 1;

    for(int i = 0; i <= p; i++){
        if(i)  C = C * (p - i + 1) % MOD * Pow(i, MOD-2LL) % MOD;
        ans = (ans + C * a[i<<1|1] % MOD) % MOD;
    }

    printf("%lld\n", ans);

    return 0;
}

总结

第一题挂了,找规律的题目做的不够多,思维好差,数学也好差,看来数学课一定要好好听啊!


只有弱者才喜欢扎堆,问题是绝大部分人都是弱者。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值