18.11.1 考试总结

今天考试又是再一次考得很屎...第一题只狗了十分...第二题十分..第三题直接挂掉了...。

但是我第三题是想出正解了的!!!但是我就是写挂了.. 少写了一句话就全挖挖了

   

 


这道题姜sir用题目中原根的做法做的,,,150行 然而yyyuuu大佬有个非常牛逼的做法只有50行

首先根据期望的定义 答案的计算方法一定是 贡献 / 概率 概率显然是$n^{m}$

所以主要问题就是计算贡献了 也就是所有的$a[i] * x % mod$进行若干次操作之后的答案和 考虑$dp$求这个东西

$dp[i][j]$表示到了第$i$次操作模$mod$之后余$j$的情况数 那么最终答案就是$∑dp[m][j] * j$

转移是$dp[i][j] -> dp[i  + 1][j * a[k] Mod mod]$但是由于第一维过大 是$1e9$的范围 所以考虑二进制优化这维

这个时候只需要$dp[j]$表示到目前对应$m$的第$i$位 也就是进行了$1 << i$次操作之后对应余数为$j$的情况数

那么考虑两个区间是如何合并的 对于两个区间中的$dp$值 $dp[i]$与$dp[j]$可以使用乘法原理更新$dp[i * j Mod mod]$

若$m$当前这一位是$1$就统计$dp$的贡献即可 和快速幂的思想是一样的

代码

#include <bits/stdc++.h> 
using namespace std;

typedef long long ll;
const int N = 1e5 + 5;
const ll MOD = 1e9 + 7;
ll g[N], f[N], h[N], n, m, mod;

ll fast_pow(ll a, ll b) {
    
    ll ans = 1;
    for(; b; b >>= 1, a = a * a % MOD)
        if(b & 1) ans = ans * a % MOD;
    return ans;
}

int main( ) {
    
    freopen("rand.in", "r", stdin);
    freopen("rand.out", "w", stdout);
    scanf("%lld%lld%lld",& n,& m,& mod);
    for(int i = 1;i <= n;i ++) {int x; scanf("%d", & x); f[x] ++;}
    g[1] = 1; ll M = m;
    while(m) {
        if(m & 1) {
            for(int i = 0;i < mod;i ++) h[i] = 0;
            for(int i = 0;i < mod;i ++) 
                for(int j = 0;j < mod;j ++) 
                    h[i * j % mod] = (h[i * j % mod] + g[i] * f[j] % MOD) % MOD;
            for(int i = 0;i < mod;i ++) g[i] = h[i];
        }
        for(int i = 0;i < mod;i ++) h[i] = 0;
        for(int i = 0;i < mod;i ++)
            for(int j = 0;j < mod;j ++) 
                h[i * j % mod] = (h[i * j % mod] + f[i] * f[j] % MOD) % MOD;
        for(int i = 0;i < mod;i ++) f[i] = h[i];
        m >>= 1;
    }
    ll ans = 0;
    for(int i = 1;i < mod;i ++) ans = (ans + i * g[i] % MOD) % MOD;
    ans = ans * fast_pow(fast_pow(n, M), MOD - 2) % MOD;
    printf("%lld\n", ans);
}

 

       

           

这道题样例又臭又长

输入

6 
2 2
3 
1 1 0 
1 2 10 
2 1 20 
2 3
5 
1 1 0 
1 2 10 
1 3 20 
2 1 30 
2 3 40 
2 2
3 
1 1 20 
1 2 10 
2 1 0 
3 3
4 
1 1 0 
1 3 10 
3 1 10 
3 3 20 
2 2
4 
1 1 0 
1 2 10 
2 1 30 
2 2 20 
1 1
 1 
1 1 -1 

输出

Yes
No
No
Yes
No
No

这道题谁来告诉我他是怎么通过网格图想到带权并查集的!!!!

首先考虑每个$2 * 2$的正方形满足$l1 + r2 = l2 + r1 -> l1 - l2 = r1 - r2$ 也就是说相邻两行的差是处处相等的 可以推出任意两行的差都相等 列也是一样的

所以对于列相等的两个点 他们所在的行可以使用带权并查集进行合并 某个行的路径权值记作他到他父亲那一行的$val$之差

所以通过给定的条件进行合并与判断是否合法 若两行之前未合并 将其合并即可 否则则判断他们目前的差是否等于他们的路径权值之差

而对于路径权值的合并 假设是由$a$合向$b$ 就有$w[fa_a] = val + w[b] - w[a]$ 画图会比较好推出来

这个时候解决了行之间合并的问题 现在问题还有检查是否非负 所以对于每一个点 都求出它所对应的父亲行里的最小点权 在对于每一行求出一个它对应的差值最大的行

所以每一行的最小点权减去最大差值取$min$就可以得到最小点权 判断他是否非负即可。

代码

#include <bits/stdc++.h>
#define oo 1e9
using namespace std;

const int N = 1e5 + 5;
int fax[N], fay[N], wx[N], wy[N], T, R, C;
int n, mi1[N], mi2[N];
struct node {
    int x, y, val;
    void read( ) {
        scanf("%d%d%d",& x,& y,& val);
    }
}P[N];

bool cmpx(const node & a, const node & b) {
    return a.x < b.x;
}

bool cmpy(const node & a, const node & b) {
    return a.y < b.y;
}

int find_x(int u) {
    
    if(fax[u] == u) return u;
    int rt = find_x(fax[u]);
    wx[u] += wx[fax[u]];
    return fax[u] = rt;
}

bool link_x(int u, int v, int w) {
    
    int fu = find_x(u), fv = find_x(v);
    if(fu != fv) {
        fax[fu] = fv;
        wx[fu] = w + wx[v] - wx[u]; return true;
    }
    else return wx[u] == wx[v] + w;
}

int find_y(int u) {
    
    if(fay[u] == u) return u;
    int rt = find_y(fay[u]);
    wy[u] += wy[fay[u]];
    return fay[u] = rt;
}

bool link_y(int u, int v, int w) {
    
    int fu = find_y(u), fv = find_y(v);
    if(fu != fv) {
        fay[fu] = fv;
        wy[fu] = w + wy[v] - wy[u]; return true;
    }
    else return wy[u] == wy[v] + w;
}

int main( ) {
    
    freopen("then.in", "r", stdin);
    freopen("then.out", "w", stdout);
    scanf("%d", & T);
    while(T --) {
        bool tag = true;
        scanf("%d%d%d",& R,& C,& n);
        for(int i = 1;i <= R;i ++) {
            fax[i] = i; wx[i] = 0; 
        }
        for(int i = 1;i <= C;i ++) {
            fay[i] = i; wy[i] = 0;
        }
        for(int i = 1;i <= n;i ++) {
            P[i].read( );
            if(P[i].val < 0) tag = false;
        }
        sort(P + 1, P + n + 1, cmpx);
        for(int i = 1;i < n;i ++) {
            if(P[i].x == P[i + 1].x) 
                if(! link_y(P[i].y, P[i + 1].y, P[i + 1].val - P[i].val)) tag = false;
        }
        sort(P + 1, P + n + 1, cmpy);
        for(int i = 1;i < n;i ++) {
            if(P[i].y == P[i + 1].y)
                if(! link_x(P[i].x, P[i + 1].x, P[i + 1].val - P[i].val)) tag = false;
        }
        memset(mi1, 0x3f3f, sizeof(mi1));
        memset(mi2, 0x3f3f, sizeof(mi2));
        for(int i = 1;i <= n;i ++) {
            int rt = find_x(P[i].x);
            mi1[rt] = min(mi1[rt], P[i].val + wx[P[i].x]);
        }
        for(int i = 1;i <= R;i ++) {
            int rt = find_x(i);
            mi2[rt] = min(mi2[rt], -wx[i]);
        }
        int cmp = oo;
        for(int i = 1;i <= R;i ++) cmp = min(cmp, mi1[i] + mi2[i]);
        if(cmp < 0) tag = false;
        if(tag) printf("Yes\n");
        else printf("No\n");
    }
}

 

       

“今年的枪毙名单已经确定了”

这道题是最简单的!! 就是一个贪心啊 首先可以发现魔法师走路是毫无用处的 距离依旧在减小 并且伤害也在减小

所以就求出厨师距离 能够攻击就攻击 不够就回能量 每次记者选择最优路径即可.. 我少写了消耗能量那一句话...。

代码

#include <bits/stdc++.h>
#define il inline
#define rg register
using namespace std;

typedef long long ll;
int xx, yy, x2, y2, d1, d2, c, d, T, a, b;

il int read( ) {
    
    int t = 1, ans = 0;
    char x; x = getchar( );
    while(x < '0' || x > '9') {
        if(x == '-') t = -1;
        x = getchar( );
    }
    while(x >= '0' && x <= '9') {
        ans = ans * 10 + x - '0';
        x = getchar( );
    }
    return ans * t;
}

il bool solve( ) {
    
    xx = read( ), yy = read( ), x2 = read( ), y2 = read( );
    c = read( ), d = read( );
    if(xx == x2 && yy == y2) {
        if(d <= 0) return true; 
        return false;
    }
    int d1 = abs(xx - x2);
    int d2 = abs(yy - y2);
    while(1) {
        if(d <= 0) return true;
        if(!d1 && !d2) return false;
        int del = d1 * d1 + d2 * d2;
        if(c < a) c += b;
        else d -= del, c -= a;
        if(d <= 0) return true;
        int x, y;
        for(int i = 1; i <= 2; i ++) {
            if(d1 == 0 && d2 == 0)    return false;
            if(d1 <= d2)    d2 --;
            else d1 --;    
        }
    }
}

int main( ) {
    
    freopen("do.in", "r", stdin);
    freopen("do.out", "w", stdout);
    T = read( ), a = read( ), b = read( );
    while(T --) {
        bool ans = solve( );
        if(ans) printf("NAIVE\n");
        else printf("SIMPLE\n");
    }
}

猜你喜欢

转载自www.cnblogs.com/Rubenisveryhandsome/p/9891326.html