HNOI2012解题报告

来源:互联网 发布:诸暨行知小学怎么样 编辑:程序博客网 时间:2024/05/21 01:28

HNOI2012解题报告

Author: Pengyihao


Day1 T1 双十字


思路

因为矩阵总的大小不超过 1000000,所以我们可以预处理往左最多能延续多少,往右、往上、往下……

然后我们考虑枚举双十字中间线所在的列。

枚举下面这根横线所在的行。

然后对于这根横线形成的双十字的数量有影响的行,一定在其之上并且长度短于它。

于是对于每段连续的 1,我们维护一个 splay

splay 的关键字键值为左右延伸的长度,然后记录每个点往上扩展的长度以及自身的长度能够给予的贡献。

这个贡献可以求一下子树的和。

那么每次计算下面这根横线的贡献的时候,直接找到它在 splay 中的位置,把它左边、右边的区间提取出来,计算贡献即可。

注意必须在第 i 行的时候插入第 i2 行,因为两根横线不相邻。


代码

#include <bits/stdc++.h>typedef long long LL;#define FOR(i, a, b) for (LL i = (a), i##_END_ = (b); i <= i##_END_; i++)#define DNF(i, a, b) for (LL i = (a), i##_END_ = (b); i >= i##_END_; i--)template <typename Tp> void in(Tp &x) {    char ch = getchar(); x = 0;    while (ch < '0' || ch > '9') ch = getchar();    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}using std::vector;const LL MAXN = 10010, MOD = 1000000009;vector<LL>line[MAXN];vector<LL>lr[MAXN], tp[MAXN], co[MAXN];LL r, c, n;struct Node {    Node *ch[2], *fa;    LL data1, data2, sum1, sum2, sum3, sz;    void clear();    void update();    void rotate();    void insert(LL, LL);    LL get_rnk(LL);    void splay(Node*);    Node *find_key(LL);} *treap;void Node::rotate(){    Node *pa = fa;    fa = pa -> fa; pa -> fa = this;    if (fa != NULL) {        bool t = (fa -> ch[0] == pa ? 0 : 1);        fa -> ch[t] = this;    }    bool t = (pa -> ch[0] == this ? 0 : 1);    Node *chd = ch[t ^ 1];    ch[t ^ 1] = pa; pa -> ch[t] = chd;    if (chd != NULL) chd -> fa = pa;    pa -> update(); update();}void Node::splay(Node *top){    while (fa != top) {        if (fa -> fa != top) {            bool t = (fa -> fa -> ch[0] == fa ? 0 : 1);            if (fa -> ch[t] == this) fa -> rotate(), rotate();            else rotate(), rotate();        }        else rotate();    }    if (top == NULL) treap = this;}LL Node::get_rnk(LL now){    Node *x = this;    LL ret = 0;    while (true) {        if (x -> data1 == now) {            return (x -> ch[0] == NULL ? 1 : x -> ch[0] -> sz + 1) + ret;        }        if (x -> data1 > now) {            if (x -> ch[0] == NULL) return ret;            x = x -> ch[0];        }        else {            if (x -> ch[1] == NULL) return x -> sz + ret;            ret += (x -> ch[0] == NULL ? 1 : x -> ch[0] -> sz + 1);            x = x -> ch[1];        }    }}Node* Node::find_key(LL rnk){    Node *x = this;    while (true) {        if ((x -> ch[0] == NULL ? 1 : x -> ch[0] -> sz + 1) == rnk) return x;        else if ((x -> ch[0] == NULL ? 1 : x -> ch[0] -> sz + 1) < rnk) {            rnk -= (x -> ch[0] == NULL ? 1 : x -> ch[0] -> sz + 1);            x = x -> ch[1];        }        else x = x -> ch[0];    }}void Node::insert(LL now, LL now2){    if (treap == NULL) {        Node *hr = new Node;        hr -> ch[0] = hr -> ch[1] = hr -> fa = NULL;        hr -> data1 = now; hr -> data2 = now2; treap = hr; treap -> update();        return;    }    LL rnk = treap -> get_rnk(now);    if (rnk == 0) {        Node *hr = new Node;        hr -> ch[0] = hr -> fa = NULL;        hr -> ch[1] = treap; hr -> data1 = now; hr -> data2 = now2;        treap -> fa = hr; hr -> update();        treap = hr;    }    else if (rnk == treap -> sz) {        Node *hr = new Node;        hr -> ch[1] = hr -> fa = NULL;        hr -> ch[0] = treap; hr -> data1 = now; hr -> data2 = now2;        treap -> fa = hr; hr -> update();        treap = hr;     }    else {        Node *hr = new Node;        treap -> find_key(rnk) -> splay(NULL);        treap -> find_key(rnk + 1) -> splay(treap);        hr -> data1 = now;        hr -> data2 = now2;        hr -> ch[0] = NULL;        hr -> ch[1] = treap -> ch[1];        hr -> fa = treap; treap -> ch[1] = hr;        hr -> ch[1] -> fa = hr; hr -> update(); treap -> update();    }}void Node::clear(){    if (ch[0] != NULL) ch[0] -> clear();    if (ch[1] != NULL) ch[1] -> clear();    delete this;}void Node::update(){    sz = 1;    sum1 = data1 * data2 % MOD;    sum2 = (data1 * (data1 + 1) / 2) * data2 % MOD;    sum3 = data2;    if (ch[0] != NULL) {        sz += ch[0] -> sz;        sum1 = (sum1 + ch[0] -> sum1) % MOD;        sum2 = (sum2 + ch[0] -> sum2) % MOD;        sum3 = (sum3 + ch[0] -> sum3) % MOD;    }    if (ch[1] != NULL) {        sz += ch[1] -> sz;        sum1 = (sum1 + ch[1] -> sum1) % MOD;        sum2 = (sum2 + ch[1] -> sum2) % MOD;        sum3 = (sum3 + ch[1] -> sum3) % MOD;    }}int main(){    freopen("cross.in", "r", stdin);    freopen("cross.out", "w", stdout);    in(r); in(c); in(n);    FOR(i, 0, r + 1) {        lr[i].resize(c + 2);        tp[i].resize(c + 2);        co[i].resize(c + 2);        line[i].resize(c + 2);    }    FOR(i, 1, r) FOR(j, 1, c) line[i][j] = 1;    FOR(i, 1, n) {        LL x, y;        in(x); in(y);        line[x][y] = 0;    }    FOR(i, 1, r) {        FOR(j, 1, c) {            if (line[i][j] == 0) continue;            if (j == 1 || line[i][j - 1] == 0) lr[i][j] = 0;            else lr[i][j] = lr[i][j - 1] + 1;        }        DNF(j, c, 1) {            if (line[i][j] == 0) continue;            if (j == c || line[i][j + 1] == 0) lr[i][j] = 0;            else chkmin(lr[i][j], lr[i][j + 1] + 1);        }        FOR(j, 1, c) if (line[i][j]) {            if (i == 1 || !line[i - 1][j]) tp[i][j] = 0;            else tp[i][j] = tp[i - 1][j] + 1;        }    }    DNF(i, r, 1) {        FOR(j, 1, c) if (line[i][j]) {            if (i == r || !line[i + 1][j]) co[i][j] = 0;            else co[i][j] = co[i + 1][j] + 1;        }    }    treap = NULL;    bool is_cleared = true;    LL ans = 0;    FOR(j, 1, c) {        if (!is_cleared) {            treap -> clear();            treap = NULL;            is_cleared = true;        }        FOR(i, 1, r) {            if (i > 2) {                if (lr[i - 2][j] != 0 && line[i - 1][j]) {                    treap -> insert(lr[i - 2][j], tp[i - 2][j]);                    is_cleared = false;                }            }            if (!line[i][j]) {                if (!is_cleared) {                    treap -> clear();                    treap = NULL;                    is_cleared = true;                }                continue;            }            LL now = lr[i][j];            if (now && treap != NULL) {                LL rnk = treap -> get_rnk(now);                if (rnk != 0) {                    Node *hr;                    if (rnk != treap -> sz) {                        treap -> find_key(rnk + 1) -> splay(NULL);                        treap -> find_key(rnk) -> splay(treap);                        hr = treap -> ch[0];                    }                    else {                        treap -> find_key(rnk) -> splay(NULL);                        hr = treap;                    }                    LL fst = hr -> sum1 * now % MOD * co[i][j] % MOD;                    LL sec = hr -> sum2 * co[i][j] % MOD;                    ans = (ans + fst - sec) % MOD;                }                if (rnk != treap -> sz) {                    Node *hr;                    if (rnk != 0) {                        treap -> find_key(rnk) -> splay(NULL);                        treap -> find_key(rnk + 1) -> splay(treap);                        hr = treap -> ch[1];                    }                    else {                        treap -> find_key(rnk + 1) -> splay(NULL);                        hr = treap;                    }                    LL valu =                        (now * now - now * (now + 1) / 2) * co[i][j] % MOD;                    ans = (ans + hr -> sum3 * valu % MOD) % MOD;                }            }        }    }    printf("%lld\n", ans);    return 0;}

Day1 T2 与非


思路

首先可以发现一个性质——NAND操作可以构成所有的位运算,这个手玩一下就出来了。

然后对于二进制的某两位,我们发现如果对于每一个操作数,他们的这两位都相同,那么不论怎么运算最后肯定还是相同的。

除了这种限制之外,就没有其它限制了。

意思是说,如果在必定相同的两位之间连一条边,那么会形成一个个联通块。联通块与联通块之间两两对于答案的贡献是独立的。

联通块可以用并查集维护。

于是就可以答案进行数位DP辣!!

对于答案的第 i 位,它的取值会影响一个联通块的取值。

我们从高到低地做,如果边界的该位上为 1,那么我们取 0 的话答案就要加上 2p,其中 p 是尚未确定的联通块个数。

其余的类似。


代码

#include <bits/stdc++.h>typedef long long LL;#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)template <typename Tp> void in(Tp &x) {    char ch = getchar(), f = 1; x = 0;    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();    if (ch == '-') f = -1, ch = getchar();    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();    x *= f;}template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}const LL MAXN = 1010;LL n, k, l, r, tot, A[MAXN], fa[MAXN];LL find(LL x){    LL tmp = x, pre;    while (tmp != fa[tmp]) tmp = fa[tmp];    while (x != tmp) pre = fa[x], fa[x] = tmp, x = pre;    return tmp;}void merge(LL x, LL y){    LL fx = find(x), fy = find(y);    if (fx != fy) fa[fx] = fy;}LL query(LL x){    if (x < 0) return 0;    LL ret = 0, tmp = tot;    static bool chose[100];    static bool how_chose[100];    memset(chose, false, sizeof chose);    if (x > (1ll << (k)) - 1) return (1ll << tmp);    DNF(i, k, 1) {        if (!(x & (1ll << (i - 1)))) {            LL fx = find(i);            if (chose[fx]) {                if (how_chose[fx] == 1) return ret;            }            else {                chose[fx] = true;                how_chose[fx] = 0;                tmp--;                continue;            }        }        else {            LL fx = find(i);            if (chose[fx]) {                if (how_chose[fx] == 1) continue;                else {                    ret += (1ll << tmp);                    return ret;                }            }            else {                chose[fx] = true;                how_chose[fx] = 1;                tmp--;                ret += (1ll << tmp);                continue;            }        }    }    return ret + 1;}int main(){    freopen("nand.in", "r", stdin);    freopen("nand.out", "w", stdout);    in(n); in(k); in(l); in(r);    FOR(i, 1, n) in(A[i]);    FOR(i, 1, k) fa[i] = i;    FOR(i, 1, k) {        LL tmp = (1ll << (k)) - 1;        FOR(j, 1, n) {            if (A[j] & (1ll << (i - 1))) {                tmp &= A[j];            }            else tmp &= (A[j] ^ ((1ll << k) - 1));        }        if (!(tmp & (1ll << (i - 1)))) {            puts("WA");        }        FOR(j, 1, k) if (j != i) {            if (bool(tmp & (1ll << (j - 1))) == bool(tmp & (1ll << (i - 1))))                merge(i, j);        }    }    FOR(i, 1, k) if (fa[i] == i) tot++;    printf("%lld\n", query(r) - query(l - 1));    return 0;}

Day1 T3 排队


思路

首先放男同学,有 n!

然后放老师,可以放到一起或分开放,方案分别为 n!×P2n+1×Pmn+3,和 n!×2(n+1)

最后放女同学。如果老师放在一起了,那么就要放一个女同学在老师中间;否则把老师看作男同学。

总方案为

n!×(Pnn+1×Pmn+3+2(n+1)×Pm1n+2×m)


代码

#include <iostream>#include <cstdio>#include <cstring>#define Max(x,y) ((x)>(y)?(x):(y))#define LL long long#define MOD 100000000using namespace std;struct bign{    int len;    LL s[10000];    bign(){        len=1;        memset(s,0,sizeof s);         s[1]=1;    }    bign operator = (const LL &num){        len=1;        s[1]=num;        return *this;    }    bign operator + (const bign&num){        bign c;c.s[1]=0;c.len=Max(num.len,len);        for(int i=1;i<=c.len;i++){            c.s[i+1]=(c.s[i]+s[i]+num.s[i])/MOD;            c.s[i]=(c.s[i]+s[i]+num.s[i])%MOD;        }        if(c.s[c.len+1])c.len++;        return c;    }    bign operator - (const bign&num){        bign c;c.s[1]=0;c.len=len;        int x=0;        for(int i=1;i<=c.len;i++){            c.s[i]=s[i]-num.s[i]+x;            x=0;            if(c.s[i]<0){                c.s[i]+=MOD;                x=-1;            }        }        while(!c.s[c.len]&&c.len>1)c.len--;        return c;    }    bign operator * (const LL&num){        bign c;c.s[1]=0;c.len=len;        for(int i=1;i<=c.len;i++){            c.s[i+1]=(c.s[i]+s[i]*num)/MOD;            c.s[i]=(c.s[i]+s[i]*num)%MOD;        }        if(c.s[c.len+1])c.len++;        return c;     }    bign operator * (const bign&num){        bign c;c.s[1]=0;c.len=len+num.len+1;        for(int i=1;i<=len;i++)            for(int j=1;j<=num.len;j++){                c.s[i+j]+=(c.s[i+j-1]+s[i]*num.s[j])/MOD;                c.s[i+j-1]=(c.s[i+j-1]+s[i]*num.s[j])%MOD;            }        while(!c.s[c.len]&&c.len>1)c.len--;        return c;    }    void out(){        for(int i=len;i>=1;i--){            if(i==len)printf("%lld",s[i]);            else printf("%08lld",s[i]);        }    }};LL n,m,tmp,tmp2;bign ans1,ans2;int main(){    freopen("queue.in", "r", stdin);    freopen("queue.out", "w", stdout);    scanf("%lld%lld",&n,&m);tmp=n+4;tmp2=n+3;    for(LL i=1;i<=n;i++){        ans1=ans1*i;        ans2=ans2*i;    }    ans1=ans1*(n+1)*n;    for(LL i=1;i<=m&&tmp>=0;i++){        tmp--;        ans1=ans1*tmp;    }    for(LL i=1;i<=m-1&&tmp2>=0;i++){        tmp2--;        ans2=ans2*tmp2;     }    ans2=ans2*2*m*(n+1);    (ans1+ans2).out();     return 0;}

Day1 T4 矿场搭建


思路

首先双联通缩点,然后对于每个联通块,只需要每个叶子节点放一个出口就行了(不能放割点)。

方案个数的话乘法原理就好了。

注意只有一个双联通分量的情况要特判!!


代码

#include <bits/stdc++.h>const int MAXN = 1010, MAXM = 510;bool is[MAXN];int belong[MAXN];int dfn[MAXN], low[MAXN], INDEX, dot[MAXN], all, sz[MAXN];int n, cnt, head[MAXN], nxt[MAXM << 1], data[MAXM << 1], du[MAXN];template <typename Tp> void in(Tp &x) {    char ch = getchar(); x = 0;    while (ch != EOF && (ch < '0' || ch > '9')) ch = getchar();    if (ch == EOF) exit(0);    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}void add(int x, int y) {    nxt[cnt] = head[x]; data[cnt] = y; head[x] = cnt++;    nxt[cnt] = head[y]; data[cnt] = x; head[y] = cnt++;}void dfs(int now, int rot, int fa) {    int size = 0; bool flag = false;    dfn[now] = low[now] = ++INDEX;    for (int i = head[now]; i != -1; i = nxt[i])         if (!dfn[data[i]]) {            size++;            dfs(data[i], rot, now);            low[now] = Min(low[now], low[data[i]]);            if (low[data[i]] >= dfn[now]) flag = true;        }        else if (data[i] != fa) {            low[now] = Min(low[now], dfn[data[i]]);        }    if (flag && (now != rot || size >= 2)) {        dot[++dot[0]] = now;        is[now] = true;    }}void dfs2(int now) {    dfn[now] = 1;    for (int i = head[now]; i != -1; i = nxt[i])        if (!is[data[i]] && !dfn[data[i]]) {            dfs2(data[i]);            sz[all]++;            belong[data[i]] = all;        }}int TTT;int main() {    freopen("mining.in", "r", stdin);    freopen("mining.out", "w", stdout);    while (true) {        in(n);        int dian = -1;        if (!n) return 0;        cnt = 0; all = 0;        memset(du, 0, sizeof du);        memset(is, 0, sizeof is);        memset(dot, 0, sizeof dot);        memset(dfn, 0, sizeof dfn);        memset(low, 0, sizeof low);        memset(head, -1, sizeof head);        memset(belong, 0, sizeof belong);        for (int i = 1; i <= n; i++) {            int s, t;            in(s); in(t); add(s, t);            dian = std::max(dian, std::max(s, t));        }        for (int i = 1; i <= dian; i++)            if (!dfn[i]) dfs(i, i, -1);        memset(dfn, 0, sizeof dfn);        for (int i = 1; i <= dian; i++)            if (!dfn[i] && !is[i]) {                belong[i] = ++all;                sz[all] = 1;                dfs2(i);            }        if (all == 1) {            printf("Case %d: %d %d\n", ++TTT, 2, dian * (dian - 1) / 2);            continue;        }        for (int i = 1; i <= dian; i++)            if (is[i]) {                for (int j = head[i]; j != -1; j = nxt[j])                    dfn[belong[data[j]]] = 0;                for (int j = head[i]; j != -1; j = nxt[j])                    if (!dfn[belong[data[j]]]) {                        dfn[belong[data[j]]] = true;                        du[belong[data[j]]]++;                    }            }        int tot = 0;        long long fang = 1;        for (int i = 1; i <= all; i++) {            if (du[i] == 1) {                tot++;                if (sz[i] > 1)                    fang = fang * (sz[i]);            }        }        printf("Case %d: %d %lld\n", ++TTT, tot, fang);    }    return 0;}

Day2 T1

这是一道计算几何的题目。

因为我还没有进行这个专题,所以我跳过了这道题目。


Day2 T2

这是一道计算几何的题目。

因为我还没有进行这个专题,所以我跳过了这道题目。


Day2 T3


思路

这个题目是一道 splay 的启发式合并的模板题。

只需要从小的合并到大的中间就可以了。


代码

#include <bits/stdc++.h>typedef long long LL;#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)template <typename Tp> void in(Tp &x) {    char ch = getchar(); x = 0;    while (ch < '0' || ch > '9') ch = getchar();    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}const int MAXN = 100010;char command[10];int n, m, rnk[MAXN], fa[MAXN];struct Node {    int data, sz, id;    Node *ch[2], *fa;    void update();    void splay(Node*);    void rotate();    Node *find_key(int);    void insert(Node*);} *to[MAXN];void Node::rotate(){    Node *pa = fa;    fa = pa -> fa;    if (fa != NULL) {        bool t = fa -> ch[0] == pa ? 0 : 1;        fa -> ch[t] = this;    }    pa -> fa = this;    bool t = pa -> ch[0] == this ? 0 : 1;    Node *chd = ch[t ^ 1];    pa -> ch[t] = chd;    if (chd != NULL) chd -> fa = pa;    ch[t ^ 1] = pa;    pa -> update(); update();}void Node::splay(Node *top){    while (fa != top) {        if (fa -> fa != top) {            bool t = (fa -> ch[0] == this ? 0 : 1);            if (fa -> fa -> ch[t] == fa) {                fa -> rotate(); rotate();            }            else rotate(), rotate();        }        else rotate();    }}Node* Node::find_key(int rnk){    Node *hr = this;    while (true) {        int nowrnk;        nowrnk = (hr -> ch[0] == NULL ? 1 : hr -> ch[0] -> sz + 1);        if (nowrnk == rnk) return hr;        if (nowrnk <  rnk) {            rnk -= nowrnk;            hr = hr -> ch[1];        }        else hr = hr -> ch[0];    }}void Node::update(){    sz = 1;    if (ch[0] != NULL) sz += ch[0] -> sz;    if (ch[1] != NULL) sz += ch[1] -> sz;}void Node::insert(Node *x){    bool t;    Node *pos = this, *pa = NULL;    while (pos != NULL) {        pa = pos;        if (x -> data > pos -> data) pos = pos -> ch[1], t = 1;        else pos = pos -> ch[0], t = 0;    }    x -> fa = pa;    if (pa != NULL) pa -> ch[t] = x;    x -> ch[0] = x -> ch[1] = NULL;    while (x != NULL) {        x -> update();        x = x -> fa;    }}void Ins(Node* now, Node* to){    if (now == NULL) return;    Ins(now -> ch[0], to); Ins(now -> ch[1], to);    to -> splay(NULL); to -> insert(now); now -> splay(NULL);}int find(int x){    int tmp = x, pre;    while (tmp != fa[tmp]) tmp = fa[tmp];    while (x != tmp) pre = fa[x], fa[x] = tmp, x = pre;    return tmp;}bool merge(int x, int y){    int fx = find(x), fy = find(y);    return fx == fy ? false : fa[fx] = fy, true;}int main(){    freopen("neverland.in", "r", stdin);    freopen("neverland.out", "w", stdout);    in(n); in(m);    FOR(i, 1, n) fa[i] = i;    FOR(i, 1, n) in(rnk[i]);    FOR(i, 1, n) {        to[i] = new Node;        to[i] -> data = rnk[i];        to[i] -> sz = 1; to[i] -> id = i;        to[i] -> fa = to[i] -> ch[0] = to[i] -> ch[1] = NULL;    }    FOR(i, 1, m) {        int x, y; in(x); in(y);        if (merge(x, y)) {            if (to[x] == NULL) printf("%d\n", x);            to[x] -> splay(NULL);            to[y] -> splay(NULL);            if (to[x] -> sz < to[y] -> sz) {                Ins(to[x], to[y]);            }            else Ins(to[y], to[x]);        }    }    int q; in(q);    while (q--) {        scanf("%s", command);        if (command[0] == 'Q') {            int x, k;            in(x); in(k);            to[x] -> splay(NULL);            if (to[x] -> sz < k) printf("-1\n");            else {                printf("%d\n", to[x] -> find_key(k) -> id);            }        }        else {            int x, y;            in(x); in(y);            if (merge(x, y)) {                to[x] -> splay(NULL);                to[y] -> splay(NULL);                if (to[x] -> sz < to[y] -> sz) {                    Ins(to[x], to[y]);                }                else Ins(to[y], to[x]);            }        }    }    return 0;}

Day2 T4 集合选数


思路

考虑一张表,将 1 在左下角。

满足一个性质:对于一个格子,它的右边的格子里的数字是它的 3 倍,上面的格子里的数字是它的 2 倍。

于是问题转化为在格子中选不相邻的数的方案数。

表的长宽不会很大,是 log 级别的。

可能有很多张不相交的表。

状压dp就好了。


代码

#include <bits/stdc++.h>typedef long long LL;#define FOR(i, a, b) for (register int i = (a), i##_END_ = (b); i <= i##_END_; i++)#define DNF(i, a, b) for (register int i = (a), i##_END_ = (b); i >= i##_END_; i--)template <typename Tp> void in(Tp &x) {    char ch = getchar(); x = 0;    while (ch < '0' || ch > '9') ch = getchar();    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}const int MOD = 1000000001;const int MAXN = 100010;bool vis[MAXN];int n, ans = 1;int map[18][18], end[18], f[2][MAXN << 1];void solve(int now){    int ret = 0;    FOR(i, 1, 100000) {        if (i == 1) map[1][i] = now;        else map[1][i] = map[1][i - 1] * 2;        if (map[1][i] > n) {            //map[1][i] = -1;            break;        }        vis[map[1][i]] = true;        FOR(j, 2, 100000) {            map[j][i] = map[j - 1][i] * 3;            if (map[j][i] > n) {                //map[j][i] = -1;                break;            }            vis[map[j][i]] = true;        }    }    end[0] = 0;    bool t = 0;    int all = 1;    f[t ^ 1][0] = 1;    bool first = true;    FOR(i, 1, 100000) {        if (map[i][1] <= n) {            FOR(j, 1, 100000) if (map[i][j] > n) {end[i] = j - 1; break;}            int limits1 = (1 << end[i]) - 1, limits2 = (1 << end[i - 1]) - 1;            FOR(j, 0, limits1) {                f[t][j] = 0;                bool flag = true;                if (!first && !f[t ^ 1][j]) continue;                FOR(l, 2, end[i]) if ((j & (1 << (l - 1))) && (((j) & (1 << (l - 2))))) {                    flag = false;                    break;                }                if (!flag) continue;                FOR(k, 0, limits2) {                    bool flag = true;                    if (!f[t ^ 1][k]) continue;                    FOR(l, 1, end[i]) {                        if ((j & (1 << (l - 1))) && (k & (1 << (l - 1)))) {                            flag = false;                            break;                        }                    }                    if (flag) f[t][j] = (f[t][j] + f[t ^ 1][k]) % MOD;                }            }        }        else {            all = i - 1;            break;        }        first = false;        t ^= 1;    }    FOR(i, 0, (1 << (end[all] - 1))) ret = (ret + f[t ^ 1][i]) % MOD;    ans = 1ll * ans * ret % MOD;}int main(){    freopen("set.in", "r", stdin);    freopen("set.out", "w", stdout);    in(n);    FOR(i, 1, n) {        if (!vis[i]) solve(i);    }    printf("%d\n", ans);    return 0;}