NOI 模拟赛

三道大原题,我就直接写了

T1 scoi2016 背单词

建一个 Trie 树,递推出每个点子树里单词节点的数量,把单词节点拿出来建个树形结构,所有单词节点向他上面最近的单词节点连边,每次贪心往比较小的那边走就可以了

不建树是错的,因为会把不同的单词节点算成一个

例如:

比如左边四个单词节点就被算到了一起,应该先从左边一个一个走,镘走了右边(#上香

#include<bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    for(;!isdigit(ch);ch=getchar())if(ch == '-') f=-f;
    for(;isdigit(ch);ch=getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 1000010;
int n;
char str[maxn];
int danger[maxn], id[maxn], tr[maxn][26], dfn, size[maxn];
vector<int> sons[maxn];
void Insert(char *str) {
    int len = strlen(str), now = 0;
    for(int i=len-1;~i;i--) {
        int pp = str[i] - 'a';
        if(!tr[now][pp]) tr[now][pp] = ++dfn;
        now = tr[now][pp];
    }
    danger[now] = 1;
}
int cmp(int x, int y) {return size[x] < size[y];}
int pnt = 1;
void dfs(int x, int cur) {
    if(danger[x]) sons[cur].push_back(++pnt), size[cur = pnt] = 1;
    rep(i, 0, 25) if(tr[x][i]) dfs(tr[x][i], cur);
}
void makesize(int x) {
    for(int i=0;i<(int)sons[x].size();i++) {
        makesize(sons[x][i]);
        size[x] += size[sons[x][i]];
    }
}
int ind[maxn], pre, _tim;
LL ans;
void solve(int x, int wfa) {
    _tim++; ans += _tim - wfa; wfa = _tim;
    sort(sons[x].begin(), sons[x].end(), cmp);
    for(int i=0;i<(int)sons[x].size();i++) solve(sons[x][i], wfa);
}
int main() {
    //freopen("words.in","r",stdin);
    //freopen("words.out","w",stdout);
    n = read();
    rep(i, 1, n) {
        scanf("%s", str);
        Insert(str);
    } dfs(0, 1); makesize(1);
    //return 0;
    solve(1, 1);
    cout << ans << endl;
}
View Code

 

T2 sdoi2017 树点染色

LCT + 线段树套路题,30 分钟码完

#include<bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i ## end; ++i)
#define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i ## end; --i)
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    for(;!isdigit(ch);ch=getchar())if(ch == '-') f=-f;
    for(;isdigit(ch);ch=getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 300010;
int n, q;
int first[maxn], to[maxn << 1], nx[maxn << 1], cnt;
inline void add(int u, int v) {
    to[++cnt] = v;
    nx[cnt] = first[u];
    first[u] = cnt;
} 
inline void ins(int u, int v) {add(u, v); add(v, u);}
int anc[maxn], size[maxn], bl[maxn], pos[maxn], _tim, dep[maxn];
inline void dfs(int x) {
    size[x] = 1;
    for(int i=first[x];i;i=nx[i]) {
        if(to[i] == anc[x]) continue;
        anc[to[i]] = x; dep[to[i]] = dep[x] + 1;
        dfs(to[i]); size[x] += size[to[i]];
    }
}
inline void dfs2(int x, int col) {
    bl[x] = col; int k = 0; pos[x] = ++_tim;
    for(int i=first[x];i;i=nx[i]) 
        if(to[i] != anc[x] && size[to[i]] > size[k]) k = to[i];
    if(!k) return;
    dfs2(k, col);
    for(int i=first[x];i;i=nx[i]) 
        if(to[i] != anc[x] && to[i] != k) dfs2(to[i], to[i]);
}
int seg[maxn << 2], tag[maxn << 2], mxseg[maxn << 2];
inline void pushdown_seg(int x, int l, int r) {
    if(tag[x]) {
        int mid = (l + r) >> 1;
        tag[x << 1] += tag[x]; tag[x << 1| 1] += tag[x];
        mxseg[x << 1] += tag[x]; mxseg[x << 1| 1] += tag[x];
        tag[x] = 0;
    }
}
inline void update(int x, int l, int r, int L, int R, int v) {
    if(L <= l && r <= R) {
        tag[x] += v;
        mxseg[x] += v;
        return;
    }
    pushdown_seg(x, l, r);
    int mid = (l + r) >> 1;
    if(L <= mid) update(x << 1, l, mid, L, R, v);
    if(R > mid) update(x << 1 | 1, mid + 1, r, L, R, v);
    mxseg[x] = max(mxseg[x << 1], mxseg[x << 1 | 1]);
}
inline int querymx(int x, int l, int r, int L, int R) {
    if(L <= l && r <= R) {
        return mxseg[x];
    }
    pushdown_seg(x, l, r);
    int mid = (l + r) >> 1, ans = 0;
    if(L <= mid) ans = max(ans, querymx(x << 1, l, mid, L, R));
    if(R > mid) ans = max(ans, querymx(x << 1 | 1, mid + 1, r, L, R));
    return ans;
}
inline void Mod(int x, int opt) {
    update(1, 1, n, pos[x], pos[x] + size[x] - 1, opt);
}
inline int lca(int x, int y) {
    while(bl[x] != bl[y]) {
        if(dep[bl[x]] < dep[bl[y]]) swap(x, y);
        x = anc[bl[x]];
    }
    return dep[x] < dep[y] ? x : y;
}
inline int qn(int x) {
    return querymx(1, 1, n, pos[x], pos[x] + size[x] - 1);
}
#define ls (ch[x][0])
#define rs (ch[x][1])
int ch[maxn][2], fa[maxn], st[maxn], top, tg[maxn];
inline int isroot(int x) {return (ch[fa[x]][0] != x) && (ch[fa[x]][1] != x);}
inline void pushup(int x) {
    tg[x] = x;
    if(tg[ls]) tg[x] = tg[ls];
}
inline void rotate(int x) {
    int y = fa[x], z = fa[y];
    int l = (ch[y][1] == x), r = l ^ 1;
    if(!isroot(y))ch[z][ch[z][1] == y] = x;
    fa[x] = z; fa[y] = x; fa[ch[x][r]] = y;
    ch[y][l] = ch[x][r]; ch[x][r] = y;
    pushup(y); pushup(x);
}
inline void splay(int x) {
    while(!isroot(x)) {
        int y = fa[x], z = fa[y];
        if(!isroot(y)) {
            if(ch[z][0] == y ^ ch[y][0] == x) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}
inline void access(int x) {
    for(int y=0;x;y=x,x=fa[x]) {
        splay(x); int tmp;
        if(rs) {tmp = tg[rs];Mod(tmp, 1);}
        if(y) {tmp = tg[y];Mod(tmp, -1);}
        rs = y; pushup(rs);
    }
}
int main() {
    //freopen("tree.in","r",stdin);
    //freopen("tree.out","w",stdout);
    dep[1] = 1;
    n = read(), q = read();
    for(int i=2;i<=n;i++) {
        int u = read(), v = read();
        ins(u, v);
    } dfs(1); dfs2(1, 1);
    rep(i, 1, n) {
        fa[i] = anc[i]; tg[i] = i;
        update(1, 1, n, pos[i], pos[i], dep[i]);
    }
    while(q--) {
        int opt = read();
        if(opt == 1) {
            int x = read();
            access(x);
        }
        if(opt == 2) {
            int x = read(), y = read();
            int hx = lca(x, y);
            int t1 = querymx(1, 1, n, pos[x], pos[x]), t2 = querymx(1, 1, n, pos[y], pos[y]);
            int t3 = querymx(1, 1, n, pos[hx], pos[hx]);
            printf("%d\n", t1 + t2 - (t3 << 1) + 1);
        }
        if(opt == 3) {
            int x = read();
            printf("%d\n", qn(x));
        }
    }
}
View Code

 

T3 hnoi2017 抛硬币

考场上只写了 30 ,还是太菜了 qnq

#include<bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    for(;!isdigit(ch);ch=getchar())if(ch == '-') f=-f;
    for(;isdigit(ch);ch=getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 100010;
LL a, b;
int mod;
int ksm(int x, int t) {
    int res = 1;
    while(t) {
        if(t & 1) res = 1LL * res * x % mod;
        x = 1LL * x * x % mod;
        t = t >> 1;
    } return res;
}
namespace solve1 {
    int cc[50][50];
    void solve() {
        cc[0][0] = 1;
        rep(i, 1, 30) cc[i][0] = 1;
        rep(i, 1, 30) rep(j, 1, 30) cc[i][j] = (cc[i - 1][j] + cc[i - 1][j - 1]) % mod;
        int maxstate = (1 << a);
        int ans = 0;
        for(int S=1;S<maxstate;S++) {
            int nS = (LL)__builtin_popcount(S) - 1;
            dwn(i, nS, 0) (ans += cc[b][i]) %= mod;
        }
        if(mod == 10) printf("%01d", ans);
        if(mod == 100) printf("%02d", ans);
        if(mod == 1000) printf("%03d", ans);
        if(mod == 10000) printf("%04d", ans);
        if(mod == 100000) printf("%05d", ans);
        if(mod == 1000000) printf("%06d", ans);
        if(mod == 10000000) printf("%07d", ans);
        if(mod == 100000000) printf("%08d", ans);
        if(mod == 1000000000) printf("%09d", ans);
        cout << endl;
    }
}

namespace solve2 {
    int cc[500][500];
    void solve() {
        cc[0][0] = 1;
        rep(i, 1, 130) cc[i][0] = 1;
        rep(i, 1, 130) rep(j, 1, 130) cc[i][j] = (cc[i - 1][j] + cc[i - 1][j - 1]) % mod;
        int ans = 0;
        rep(i, 1, a) {
            int res = 0;
            rep(j, 0, i-1) (res += cc[b][j]) %= mod;
            (ans += (1LL * cc[a][i] * res) % mod) %= mod;
        }
        if(mod == 10) printf("%01d", ans);
        if(mod == 100) printf("%02d", ans);
        if(mod == 1000) printf("%03d", ans);
        if(mod == 10000) printf("%04d", ans);
        if(mod == 100000) printf("%05d", ans);
        if(mod == 1000000) printf("%06d", ans);
        if(mod == 10000000) printf("%07d", ans);
        if(mod == 100000000) printf("%08d", ans);
        if(mod == 1000000000) printf("%09d", ans);
        cout << endl;
    }
}

int main() {
    while(scanf("%lld%lld%d", &a, &b, &mod) == 3 ) {
        mod = pow(10, mod);
        if(a <= 100 && b <= 100) solve2::solve();
        else puts("QAQ");
    }
}
30pts

30pts就是:

$\sum\limits_{i=0}^a \sum\limits_{j=0}^{b} [i > j] \times C_a^i \times C_b^j$

也就是 $\sum\limits_{i=0}^a \sum\limits_{j=0}^{a-i} C_a^{i+j} \times C_b^{i-j}$

然后根据范德蒙德卷积(链接里的第一个)得到原式就是:$\sum\limits_{i=b+1}^{b+a} C_{a+b}^i$

然后就是组合数取模方面的东西了

先把式子拆成两部分,记 $c = \lceil \frac{a+b}{2} \rceil$,则原式 = $\sum\limits_{i=c}^{a+b} C_{a+b}^i + \sum\limits_{i=b+1}^{c} C_{a+b}^i$

后一项可以暴力,前一项当 $a+b$ 是奇数的时候是杨辉三角的一行和,也就是 $2^{a+b-1}$,当 $a+b$ 是偶数的时候,是 $2^{a+b-1} - \frac{1}{2} \times C_{a+b}^{\frac{a+b}{2}}$

然后需要扩展 lucas 求组合数,然而不会

。。

补档:扩展 lucas

要求 $C_n^m \space mod \space p$,$p$ 不一定是质数

我们可以唯一分解然后用 excrt 合并一下,现在问题就是求 $C_n^m \space mod \space p^k$,$p$ 是质数

根据组合数的定义式,我们只要快速求出模意义下的阶乘就可以了

举个简单的例子:

$22! \space mod \space 3^2$

发现 $22! = (3^7) \times (1 \times 2 \times 3 \times ... \times 7) \times (1 \times 2 \times 4 \times 5 \times 7 \times 8 \times 10 \times 11 \times 13 \times 14 \times 16 \times 17 \times 19 \times 22)$

第一个括号是 $p^{\lfloor \frac{n}{p} \rfloor}$,第二个括号是 $(\lfloor \frac{n}{p} \rfloor) !$ ,递归解决,第三个括号在模意义下存在一个循环,循环了 $\lfloor \frac{n}{p^k} \rfloor$ 次,每次是 $\prod\limits_{i=1}^{p^k} i \times [gcd(i,p)==1]$ ,可以先算这一个循环,然后算它的 $\lfloor \frac{n}{p^k} \rfloor$ 次幂,最后还剩了 $n \space mod \space p^k$ 项,注意到每一项有贡献当且仅当它与 $p$ 互质,暴力算即可

算组合数的时候,可以把所有 $p$ 提到分数外面,这样就有逆元了,也就是你要算 $\frac{\frac{n!}{p^{p_1}}}{\frac{m!}{p^{p_2}} \times \frac{(n-m)!}{p^{p_3}}} \times p^{p_1-p_2-p_3}$

$p_1,p_2,p_3$ 可以递推算一下

然后在算阶乘的时候其实并不用算第一部分,因为不算第一部分算出来的是 $\frac{n!}{p^{p_1}}$,就不用除了

复杂度大概是 $O(plogp)$

#include <bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
    int x = 0, f = 1;
    char ch;
    for (ch = getchar(); !isdigit(ch); ch = getchar())
        if (ch == '-')
            f = -f;
    for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0';
    return x * f;
}
const int mod = 1e9, maxn = 2e6 + 10;
LL a, b;
int pp, k2, k5, fac2[maxn], fac5[maxn];
inline int ksm(int x, LL t, int md) {
    int res = 1;
    while (t) {
        if (t & 1)
            res = 1LL * res * x % md;
        x = 1LL * x * x % md;
        t = t >> 1;
    }
    return res;
}
inline void exgcd(LL a, LL b, LL &x, LL &y) {
        if(!b) {
                x = 1, y = 0;
                return;
        }
        exgcd(b, a%b, y, x);
        y -= a / b * x;
}
LL x, y;
inline LL inv(LL a, LL md) {
        x = 0, y = 0;
        exgcd(a, md, x, y);
        return (x + md) % md;
}
void print(int ans) {
    // cout << pp << " " << ans << endl;
    int md = pow(10, pp);
    ans %= md;
    if (pp == 1)
        printf("%01d", ans);
    if (pp == 2)
        printf("%02d", ans);
    if (pp == 3)
        printf("%03d", ans);
    if (pp == 4)
        printf("%04d", ans);
    if (pp == 5)
        printf("%05d", ans);
    if (pp == 6)
        printf("%06d", ans);
    if (pp == 7)
        printf("%07d", ans);
    if (pp == 8)
        printf("%08d", ans);
    if (pp == 9)
        printf("%09d", ans);
    cout << endl;
}
LL fc1(LL n, int type) {
    LL ans = 0, p = type ? 2 : 5;
    for (LL i = n; i; i /= p) ans += i / p;
    return ans;
}
LL fc2(LL n, int type) {
        if(!n) return 1;
        int md = type ? k2 : k5;
        int s = type ? fac2[md] : fac5[md];
        s = ksm(s, n / md, md);
        s = 1LL * s * (type ? fac2[n % md] : fac5[n % md]) % md;
        return s * fc2(n / (type ? 2 : 5), type) % md;
}
int aa, bb;
LL tms, cur, ret;
int C(LL n, LL m, int type, int div) {
    int md = type ? k2 : k5;
    if (n < m)
        return 0;
    tms = fc1(n, type) - fc1(m, type) - fc1(n - m, type);
    if (div && type) tms--;
    cur = ksm((type ? 2 : 5), tms, md);
    if(div && !type)cur = cur * inv(2, md);
    if (tms >= 9) return 0;
    ret = fc2(n, type) * inv(fc2(m, type), md) % md * inv(fc2(n - m, type), md) % md * cur % md;
    return ret;
}
int getC(LL n, LL m, int div) {
    int ans = 0;
    aa = C(n, m, 1, div), bb = C(n, m, 0, div);
    aa = aa * (mod/k2) %mod * inv(mod/k2, k2) %mod;
    ans = (ans + aa) %mod;
    
    bb = bb * (mod/k5) %mod * inv(mod/k5, k5) %mod;
    ans = (ans + bb) %mod;
    return ans;
}
int main() {
    k2 = ksm(2, 9, 1e9);
    k5 = ksm(5, 9, 1e9);
    fac2[0] = fac5[0] = 1;
    rep(i, 1, k2) if (i % 2) fac2[i] = 1LL * fac2[i - 1] * i % k2;
    else fac2[i] = fac2[i - 1];
    rep(i, 1, k5) if (i % 5) fac5[i] = 1LL * fac5[i - 1] * i % k5;
    else fac5[i] = fac5[i - 1];
    while (scanf("%lld%lld%d", &a, &b, &pp) == 3) {
        int ans = ksm(2, a + b - 1, 1e9);
        for (LL i = b + 1; i <= (a + b) / 2; i++) (ans += getC(a + b, i, 0)) %= mod;
        if (!((a + b) & 1))
            (ans += (mod - getC(a + b, ((a + b) >> 1), 1))) %= mod;
        print(ans);
    }
}
100pts
#include <bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch;
    for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
    for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0';
    return x * f;
}
LL n, m, p;
inline LL ksm(LL x, LL t, LL mod) {
    LL res = 1;
    while(t) {
        if(t & 1) (res *= x) %= mod;
        (x *= x) %= mod;
        t = t >> 1;
    } return res;
}
inline void exgcd(LL a, LL b, LL &x, LL &y) {
    if(!b) {
        x = 1, y = 0;
        return;
    }
    exgcd(b, a%b, y, x);
    y -= (a / b) * x;
}
inline LL inv(LL a, LL mod) {
    LL x, y;
    exgcd(a, mod, x, y);
    return (x + mod) % mod;
}
#define mp make_pair
pair<LL, LL> a, b;
inline LL fc1(LL n, LL p) {
    int ans = 0;
    for(LL i = n; i; i /= p) ans += (i / p);
    return ans;
}
inline LL fc2(LL n, LL p, LL pr) {
    if(!n) return 1;
    LL s = 1;
    rep(i, 1, pr) if(i % p) s = 1LL * s * i % pr;
    s = ksm(s, n / pr, pr);
    rep(i, 1, n % pr) if(i % p) s = 1LL * s * i % pr;
    return s * fc2(n / p, p, pr) % pr;
}
inline LL C(LL n, LL m, LL p, LL tms) {
    LL pr = pow(p, tms), px = fc1(n, p) - fc1(m, p) - fc1(n - m, p);
    if(px >= tms) return 0;
    LL res = fc2(n, p, pr) * inv(fc2(m, p, pr), pr) % pr * inv(fc2(n - m, p, pr), pr) % pr;
    return ksm(p, px, pr) * res % pr;
}
pair<LL, LL> merge(pair<LL, LL> a, pair<LL, LL> b) {
    LL t = __gcd(a.second, b.second);
    LL gm = b.second / t, M = gm * a.second;
    pair<LL, LL> c;
    c.first = ((LL) inv(a.second / t, gm) * ((b.first - a.first) / t) % gm * a.second + a.first) % M;
    c.second = M; if(c.first < 0) c.first += M;
    return c;
}
int main() {
    cin >> n >> m >> p;
    a = mp(0, 1);
    for(LL i=2;i<=p;i++) if(p % i == 0) {
        LL tms = 0, cur = 1;
        while(p % i == 0) tms++, cur *= i, p /= i;
        b = mp(C(n, m, i, tms), cur);
        a = merge(a, b);
    }
    cout << a.first << endl;
}
组合数取模

 

代码暂时没拿到,先咕,明天补

我卢本伟没有咕咕

posted @ 2019-03-13 22:03  探险家Mr.H  阅读(209)  评论(0编辑  收藏  举报