20180806 提高Day1 训练赛

题目目录

  1. 扫雷
  2. 最大公约数
  3. 随机排列

扫雷

题意

一个 $3$ 行 $N$ 列的扫雷,已知第二行没有雷,而且你知道第二行上面的数字,求有几种合法的埋雷方案。

$T$ 组数据,$N\leq 10000, T\leq 100$ 。答案模 $10^9+7$ 。

题解

DP。 设f[i][j]表示当前在第 $i$ 列,$j$ 是一个四位状态,表示我的左上角,我的左下角,我的上面,我的下面有没有雷(这里我代表第二行第 $i$ 列)。

那么,我们枚举我的右上角和右下角放不放,然后判断一下是否合法,dp过去就可以了。

调试记录

  • 初始化错了…
  • 最后枚举到了 $n$ 而不是 $n-1$ ,导致多放置了两个雷。

代码

#pragma GCC optimize("-fdelete-null-pointer-checks,inline-functions-called-once,-funsafe-loop-optimizations,-fexpensive-optimizations,-foptimize-sibling-calls,-ftree-switch-conversion,-finline-small-functions,inline-small-functions,-frerun-cse-after-loop,-fhoist-adjacent-loads,-findirect-inlining,-freorder-functions,no-stack-protector,-fpartial-inlining,-fsched-interblock,-fcse-follow-jumps,-fcse-skip-blocks,-falign-functions,-fstrict-overflow,-fstrict-aliasing,-fschedule-insns2,-ftree-tail-merge,inline-functions,-fschedule-insns,-freorder-blocks,-fwhole-program,-funroll-loops,-fthread-jumps,-fcrossjumping,-fcaller-saves,-fdevirtualize,-falign-labels,-falign-loops,-falign-jumps,unroll-loops,-fsched-spec,-ffast-math,Ofast,inline,-fgcse,-fgcse-lm,-fipa-sra,-ftree-pre,-ftree-vrp,-fpeephole2",3)
#include <bits/stdc++.h>
using namespace std;

#ifdef _WIN32
#define LL "%I64d"
#else
#define LL "%lld"
#endif

inline int rd() { int ret=0,nag=0;char ch;while(!isdigit(ch=getchar()))nag=ch=='-';ret=ch-'0';while(isdigit(ch=getchar()))(ret*=10)+=ch-'0';return nag?-ret:ret;}
inline int rds(char*s) {char*p=s,ch;while(!isdigit(ch=getchar()));*(p++)=ch;while(isdigit(ch=getchar()))*(p++)=ch;return p-s;}

typedef long long ll;
ll f[10005][16];
char s[10005];

int count(int x) {
    int ret=0;
    while(x) {
        ret+=x&1; x >>= 1;
    }
    return ret;
}

int main() {
    int T = rd();
    while (T--) {
        memset(f, 0, sizeof(f));
        int n = rds(s);
        for (int sta=0; sta<4; sta++) f[1][sta] = count(sta)<=(s[0]-'0');
        for (int i=1; i<=n; i++)
            for (int sta=0; sta<16; sta++) {
                for (int plc=0; plc<(i==n?1:4); plc++) {
                    if (count(sta) + count(plc) != (s[i-1] - '0')) continue;
                    (f[i+1][((sta & 0x3) << 2) | plc] += f[i][sta]) %= (ll)1e9+7;
                }
            }
        ll ans=0;
        for (int sta=0; sta<16; sta++) {
            // cerr << "place " << sta << " can get " << f[n+1][sta] << endl;
            (ans += f[n+1][sta]) %= (ll)1e9+7;
        }
        printf(LL "\n", ans);
    }
    return 0;
}

最大公约数

题意

image

image

题解

首先,我们把 $a_i$ 分解质因数。

$\gcd\left(a_i, g\right)>1$,就是说,$a_i$ 和 $g$ 有至少相同的 $1$ 个质因数。

因此,我们把 $a_i$ 分解质因数,然后按因子分类。

询问的时候,我们把 $g$ 分解质因数,然后在每一种质因数内二分左端点和右端点。

现在的问题是,如何快速统计二分出来的区间内的最大值和最大值个数?

最大值我想到了RMQ,但是它不能统计最大值个数。我们可以在max的基础上再记一个sum,然后就可以统计最大值个数了。

调试记录

  • $g$ 和 $a_i$ 可能有多个相同的质因数,但是这些质因数里的 $a_i$ 只算一次。我考试时没有考虑这种情况。

代码

#pragma GCC optimize("-fdelete-null-pointer-checks,inline-functions-called-once,-funsafe-loop-optimizations,-fexpensive-optimizations,-foptimize-sibling-calls,-ftree-switch-conversion,-finline-small-functions,inline-small-functions,-frerun-cse-after-loop,-fhoist-adjacent-loads,-findirect-inlining,-freorder-functions,no-stack-protector,-fpartial-inlining,-fsched-interblock,-fcse-follow-jumps,-fcse-skip-blocks,-falign-functions,-fstrict-overflow,-fstrict-aliasing,-fschedule-insns2,-ftree-tail-merge,inline-functions,-fschedule-insns,-freorder-blocks,-fwhole-program,-funroll-loops,-fthread-jumps,-fcrossjumping,-fcaller-saves,-fdevirtualize,-falign-labels,-falign-loops,-falign-jumps,unroll-loops,-fsched-spec,-ffast-math,Ofast,inline,-fgcse,-fgcse-lm,-fipa-sra,-ftree-pre,-ftree-vrp,-fpeephole2",3)
#include <bits/stdc++.h>
#define up(a, b) a = max(a, b)
#define down(a, b) a = min(a, b)
using namespace std;

ifstream fin("gcd.in");
ofstream fout("gcd.out");

struct node {
    int v, id;
    node(int x, int y) : v(x), id(y) {}
    inline bool operator < (int res) {
        return id < res;
    }
    friend inline bool operator < (int res, const node & ref) {
        return res < ref.id;
    }
};
struct wrapper {
    int a[20];
    inline int & operator[] (int xb) { return a[xb]; }
    inline const int & operator[] (int xb) const { return a[xb]; }
};

int n, m;
vector<node> A[100005];
vector<wrapper> dp[100005];
vector<wrapper> sum[100005];
vector<int> F;
map<int, bool> E;
int mm[100005];
const int prime[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313};

inline void fracit(int x, int id) {
    int b = x;
    for (int i=0; prime[i] <= sqrt(x); ++i) {
        int p = prime[i];
        if (x % p == 0) {
            while (x % p == 0) x /= p;
            if (!(E[p])) E[p] = 1, F.push_back(p);
            A[p].push_back(node(b, id));
        }
    }
    if (x > 1) {
        int p = x;
        if (!(E[p])) E[p] = 1, F.push_back(p);
        A[p].push_back(node(b, id));
    }
}

inline void rmqit() {
    mm[0] = -1;
    for (int i=1; i<=::n; i++) {
        mm[i] = ((i & (i-1)) == 0) ? mm[i-1] + 1 : mm[i-1];
    }
    for (int i=0; i<F.size(); i++) {
        vector<node> & A = ::A[F[i]];
        vector<wrapper> & dp = ::dp[F[i]];
        vector<wrapper> & sum = ::sum[F[i]];
        int n = A.size();
        dp.resize(n + 1); sum.resize(n + 1);
        for (int i=1; i<=n; i++) dp[i][0] = A[i-1].v, sum[i][0] = 1;
        for (int j=1; j<=mm[n]; j++)
            for (int i=1; i+(1<<j)-1<=n; i++) {
                if (dp[i][j-1] > dp[i+(1<<(j-1))][j-1]) {
                    dp[i][j] = dp[i][j-1];
                    sum[i][j] = sum[i][j-1];
                } else if (dp[i][j-1] < dp[i+(1<<(j-1))][j-1]) {
                    dp[i][j] = dp[i+(1<<(j-1))][j-1];
                    sum[i][j] = sum[i+(1<<(j-1))][j-1];
                } else {
                    dp[i][j] = dp[i][j-1];
                    sum[i][j] = sum[i][j-1] + sum[i+(1<<(j-1))][j-1];
                }
            }
    }
}
inline int rmq(int P, int x, int y) {
    int k = mm[y-x+1];
    return max(dp[P][x][k],dp[P][y-(1<<k)+1][k]);
}
struct fakepair {
    int a,b;
};
inline fakepair count(int P, int x, int y) {
    if (x <= y) {
        int k = mm[y-x+1];
        fakepair ret = count(P, x+(1<<k), y);
        int u = dp[P][x][k], v = ret.a;
        if (u > v) return (fakepair){u, sum[P][x][k]};
        else if (u < v) return (fakepair){v, ret.b};
        else return (fakepair){u, sum[P][x][k] + ret.b};
    }
    return (fakepair){-1, -1};
}
inline void answer(int P, int l, int r, int & mx, int & sm) {
    if (A[P].empty()) return;
    vector<node> :: iterator it = lower_bound(A[P].begin(), A[P].end(), l);
    if (it == A[P].end()) return;
    int sp = it - A[P].begin() + 1;
    it = upper_bound(A[P].begin(), A[P].end(), r);
    int ep = it - A[P].begin();
    if (sp > ep) return;
    int lcm = rmq(P, sp, ep);
    if (lcm > mx) mx = lcm, sm = count(P, sp, ep).b;
}

int main() {
    ios :: sync_with_stdio(false);
    fin >> n >> m;
    for (int i=1; i<=n; i++) {
        int x; fin >> x;
        fracit(x, i); 
    }
    rmqit();
    while (m--) {
        int g, l, r; fin >> g >> l >> r;
        int mx=-1, sum=-1;
        for (int i=0; prime[i]<=sqrt(g); i++) {
            int p = prime[i];
            if (g % p == 0) {
                while (g % p == 0) g /= p;
                answer(p, l, r, mx, sum);
            }
        }
        if (g > 1) answer(g, l, r, mx, sum);
        fout << mx << " " << sum << endl;
    }
    return 0;
}

随机排列

题意

image

image

代码

#include <bits/stdc++.h>
#define pn(i) (i * (i-1) / 2)
using namespace std;

typedef long double ld;

ld p[105][10005], g[105][10005];
ld P[105][10005], S[105][10005];
int a[105];

int main() {
    ios :: sync_with_stdio(false);
    p[1][0] = 1;
    for (int i=1; i<=100; i++)
        for (int j=0; j<=pn(i); j++)
            for (int k=0; k<=i; k++)
                p[i+1][j+k] += p[i][j] / (i+1);
    for (int i=1; i<=100; i++) {
        P[i][0] = p[i][0]; S[1][0] = 0;
        for (int j=1; j<=pn(i); j++) {
            P[i][j] = P[i][j-1] + p[i][j];
            S[i][j] = S[i][j-1] + p[i][j] * j;
        }
    }
    for (int i=1; i<=100; i++) {
        g[i][0] = S[i][pn(i)];
        for (int j=1; j<=pn(i); j++) {
            int k = floor(j + g[i][j-1]);
            if (k > pn(i)) k = pn(i);
            g[i][j] += S[i][k] - S[i][j];
            g[i][j] -= (P[i][k] - P[i][j]) * j;
            g[i][j] += g[i][j-1] * (P[i][pn(i)] - P[i][k]);
        }
    }
    int T; cin >> T;
    while (T--) {
        int n, k; cin >> n >> k;
        for (int i=1; i<=n; i++) cin >> a[i];
        int rep = 0;
        for (int i=1; i<n; i++)
            for (int j=i+1; j<=n; j++) rep += a[i] > a[j];
        int oc = max(rep - k, 0);
        ld co = g[n][min(k-1, pn(n))];
        if (oc <= co) {
            cout << oc << ".0000000000\n";
        } else {
            cout << fixed << setprecision(10) << max((ld)0, co) << endl; 
        }
    }
    return 0;
}
posted @ 2018-08-06 20:31  MCH__ds  阅读(201)  评论(0编辑  收藏  举报