[考试总结]ZROI-21-CSP7连-DAY4 总结

#T1 扫雷

#题意简述

一个 (n imes m(n,mleq200)) 的网格,每个格子中的数字表示以该格子为中心的 (3 imes3) 的方格中地雷的数量,问总共有多少个地雷。

#大体思路

我们不需要知道地雷具体怎么摆放,所以只要找到一组点各自的势力范围能恰好覆盖整张地图即可,于是我们可以直接上下、左右各相隔三个进行选点。

#Code

const int N = 210;

int n, m, a[N][N], ans;

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++ i)
      for (int j = 1; j <= m; ++ j)
        scanf("%d", &a[i][j]);
    /*特别注意填充开始的边界*/
    for (int i = n % 3 ? 1 : 2; i <= n; i += 3)
      for (int j = m % 3 ? 1 : 2; j <= m; j += 3)
        ans += a[i][j];
    printf("%d", ans);
    return 0;
}

#T2 翻转

#题意简述

有一个 (n imes m(n,mleq100)) 的 01 矩阵,现在需要将所有位置变为 (1),改变的规则如下:

  • 对于 ((a,b)),若 (exists xin[1,n],yin[1,m]),其中 ((a,y),(x,b),(x,y)) 均为 (1),那么修改 ((a,b)) 的代价为 (3),否则为 (4).

问最小代价。

#大体思路

考虑如何判断是否 (exists xin[1,n],yin[1,m]),其中 ((a,y),(x,b),(x,y)) 均为 (1),不难发现,如果将 ((i,j)=1) 看作将第 (i) 行((L.i))和第 (j) 列((A.j))连接的话,那么意味着 (L.a,L.x,A.b,A.y) 应当在同一连通块内,而最终的状态一定有全图为一个连通块,于是我们一定需要将所有初始连通块连成一块,这需要以 (4) 的代价修改连通块数量减 (1) 个点,最后剩下的点的修改代价都为 (3).

#Code

const int N = 100010;
const int INF = 0x3fffffff;

/*用并查集维护连通块信息*/
struct DSU {
    int siz[N], f[N], tot;

    inline void init(int x) {
        tot = x;
        for (int i = 0; i <= x; ++ i)
          f[i] = i, siz[i] = 1;
    }

    inline int find(int x) {
        while (x != f[x]) x = f[x] = f[f[x]];
        return x;
    }

    inline void merge(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) return;
        if (siz[x] > siz[y]) swap(x, y);
        f[x] = y, siz[y] += siz[x], -- tot;
    }
} dsu;

int n, m, tcnt, ans; char c;

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> m; dsu.init(n + m);
    for (int i = 1; i <= n; ++ i)
      for (int j = 1; j <= m; ++ j) {
        cin >> c; if (c == '1') dsu.merge(i, j + n), ++ tcnt;
      }
    ans = (dsu.tot - 1) * 4 + (n * m - tcnt - dsu.tot + 1) * 3;
    cout << ans; return 0;
}

#T3 斜率

#题意简述

平面直角坐标系上 (n(nleq10^6)) 个点,可任选 (2) 个点连直线,找到斜率最接近 (frac P Q) 的直线的斜率。

#大体思路

我们考虑有一条斜率为 (frac PQ) 的直线在坐标系上平移,我们可以得到所有点按此方向在 (x) 轴上的投影,不难发现,斜率最接近 (frac PQ) 的两个点的投影一定相邻,这一点简单画图不难发现,于是我们直接按照投影位置进行排序即可。

#Code

#define db double
#define ll long long

const int N = 1000010;
const db INF = 1e10;

template <typename T> inline T ABS(const T &x) {return x < 0 ? -x : x;}

double tana, ans_gap = INF;
int n; ll p, q, nx, ny;

struct Point {
    ll x, y;
    bool operator < (const Point & b) const {
        return -x * p + y * q < -b.x * p + b.y * q;
    }
} a[N];

ll gcd(ll x, ll y) {return y ? gcd(y, x % y) : x;}

db calc(Point x, Point y) {
    return x.x == y.x ? INF : (db)(x.y - y.y) / (db)(x.x - y.x); 
}

int main() {
    scanf("%d%lld%lld", &n, &p, &q);
    tana = (db)p / (db)q;
    for (int i = 1; i <= n; ++ i)
      scanf("%lld%lld", &a[i].x, &a[i].y);
    sort(a + 1, a + n + 1);
    for (int i = 1; i < n; ++ i) {
        db now_gap = ABS(calc(a[i], a[i + 1]) - tana);
        if (now_gap < ans_gap) {
            ans_gap = now_gap;
            nx = ABS(a[i].x - a[i + 1].x);
            ny = ABS(a[i].y - a[i + 1].y);
        }
    }
    ll g = gcd(ny, nx);
    printf("%lld/%lld", ny / g, nx / g);
    return 0;
}

#T4 任务

#题意简述

(n(nleq 350)) 个人和 (n)不同的任务,第 (i) 个人高兴当且仅当他被分到 (i) 个任务,问将所有任务都分配下去后至少有一个人高兴的方案数((mod10^9+7))。

#大体思路

这种计数问题还是要去尝试 DP,设 (f_{i,j}) 表示前 (i) 个人分 (j) 个任务,至少有 (1) 个人高兴的方案数,考虑第 (i) 个人是否高兴。

(i) 个人高兴,则可以挑出 (i) 个直接给第 (i) 个人,剩下的 (j-i) 个可以随意的给 (i-1) 个人,于是方案总数为 (dbinom jicdot(i-1)^{j-i})

如果第 (i) 个人不高兴,那么设给了第 (i) 个人 (k(k e i)) 个任务,那么此时的方案数为前 (i-1) 个人分 (j-k) 个的方案数,于是该情况的总方案数为

[sumlimits_{k=1}^n[k e i]dbinom jkf_{i-1,j-k}, ]

#Code

#define ll long long

const int N = 510;
const int MOD = 1e9 + 7;
const int INF = 0x3fffffff;

int n; ll C[N][N], f[N][N];

inline void init_C(int x) {
    for (int i = 0; i <= x; ++ i) C[i][0] = C[i][i] = 1;
    for (int i = 1; i <= x; ++ i)
      for (int j = 1; j < i; ++ j)
        C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
}

ll fpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) (res *= a) %= MOD;
        (a *= a) %= MOD, b >>= 1;
    }
    return res;
}

int main() {
    scanf("%d", &n); init_C(n);
    for (int i = 1; i <= n; ++ i)
      for (int j = 0; j <= n; ++ j) {
          if (j >= i) f[i][j] = fpow(i - 1, j - i) * C[j][i] % MOD;
          for (int k = 0; k <= j; ++ k) {
              if (k == i) continue;
              (f[i][j] += f[i - 1][j - k] * C[j][k] % MOD) %= MOD;
          }
      }
    printf("%lld", f[n][n]);
    return 0;
}
原文地址:https://www.cnblogs.com/Dfkuaid-210/p/ZROI-21-CSP-DAY4.html