HNOI2012解题报告

8 篇文章 0 订阅
6 篇文章 0 订阅

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 100000000

using 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值