首页 > 其他分享 >2023冲刺国赛模拟 15.1

2023冲刺国赛模拟 15.1

时间:2023-06-10 22:47:12浏览次数:44  
标签:15.1 point int top max1 国赛 deep 2023 now

T1 计数

首先考虑计数有标号可重叠的方案数,容易发现此时 \(x,y\) 两维独立,因此考虑其中 \(1\) 维,设 \(f_{i,j}\) 表示此时考虑到第 \(i\) 对左右边界 \((x_{i,1},x_{i,2})\) ,离散化后的 \(x\) 坐标形成了 \(j\) 个点时的方案数,容易发现此时数轴上存在 \(j\) 个点,以及 \(j+1\) 个空白段,转移考虑 \(x_{i+1,1}\) 和 \(x_{i+1,2}\) 的位置,容易发现存在以下三种转移:

  1. \(x_{i+1,1}\) 和 \(x_{i+1,2}\) 均位于空白段内:

\[f_{i,j}\times (\binom{j+1}{2}+j+1)\to f_{i+1,j+2} \]

  1. \(x_{i+1,1}\) 和 \(x_{i+1,2}\) 一个位于原先点上,一个位于空白段内:

\[f_{i,j}\times j\times (j+1)\to f_{i+1,j+1} \]

  1. \(x_{i+1,1}\) 和 \(x_{i+1,2}\) 均位于原有点上:

\[f_{i,j}\times \binom{j}{2}\to f_{i+1,j} \]

\(i\) 个矩形的方案数显然为 \((\sum_j f_{i,j})^2\) 。

考虑计算题目要求的答案,即 \(n\) 个矩形无标号,不可重叠的方案数,设答案为 \(ans(n)\) ,设有标号,可重叠方案数为 \(g(n)\) ,设一个长度为 \(n\) 的序列,总共有 \(i\) 种元素,要求每个元素至少出现一次的合法序列的数量为 \(h(n,i)\) ,容易发现这样的等量关系:

\[g(n)=\sum_{i=1}^{n}ans(i)h(n,i) \]

直接 \(O(n^2)\) 递推即可。

code
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int max1 = 5000;
const int mod = 1e9 + 7;

int n;

int g[2][max1 * 2 + 5], h[2][max1 + 5], now;
int f[max1 + 5];

int Binom ( int x )
{
    return ( 1LL * x * ( x - 1 ) >> 1 ) % mod;
}

int Calc ( int x )
{
    return ( 1LL * ( x + 1 ) * x >> 1 ) % mod;
}

void Add ( int &x, int y )
{
    x += y;
    if ( x >= mod )
        x -= mod;
    return;
}

int Quick_Power ( int base, int p )
{
    int res = 1;
    while ( p )
    {
        if ( p & 1 )
            res = 1LL * res * base % mod;
        p >>= 1;
        base = 1LL * base * base % mod;
    }
    return res;
}

int main ()
{
    freopen("count.in", "r", stdin);
    freopen("count.out", "w", stdout);

    scanf("%d", &n);

    now = 0; g[now][0] = 1; h[now][0] = 1;
    for ( int i = 1; i <= n; i ++ )
    {
        now ^= 1;
        memset(g[now], 0, sizeof(int) * ( i + i + 1 ));
        memset(h[now], 0, sizeof(int) * ( i + 1 ));

        for ( int j = 0; j <= i + i - 2; j ++ )
        {
            g[now][j + 2] = ( g[now][j + 2] + 1LL * g[now ^ 1][j] * ( Binom(j + 1) + j + 1 ) ) % mod;
            g[now][j + 1] = ( g[now][j + 1] + 2LL * g[now ^ 1][j] * Calc(j) ) % mod;
            g[now][j] = ( g[now][j] + 1LL * g[now ^ 1][j] * Binom(j) ) % mod;
        }

        for ( int j = 0; j <= i - 1; j ++ )
        {
            h[now][j + 1] = ( h[now][j + 1] + 1LL * h[now ^ 1][j] * ( j + 1 ) ) % mod;
            h[now][j] = ( h[now][j] + 1LL * h[now ^ 1][j] * j ) % mod;
        }

        int sum = 0;
        for ( int j = 0; j <= i + i; j ++ )
            Add(sum, g[now][j]);
        sum = 1LL * sum * sum % mod;

        for ( int j = 1; j <= i - 1; j ++ )
            Add(sum, mod - 1LL * f[j] * h[now][j] % mod);

        f[i] = 1LL * sum * Quick_Power(h[now][i], mod - 2) % mod;
    }

    printf("%d\n", f[n]);

    return 0;
}

T2 AKEE的大砍刀

将原问题抽象,将含有公共边的三角形连边,容易发现形成树形结构,那么在原多边形上切一刀相当于断裂树上一条边,因此考虑维护每个小朋友吃的蛋糕所构成的虚树,对于每条边维护边权表示有多少虚树覆盖了这条边,此时我们的操作为树上一条链边权全局加,查询最小值以及最小值个数,很容易使用树剖 + 线段树维护,当然,由于笔者是废物,脑子经常断路,所以写了树剖套分块,具体而言对于每条重链分块即可,复杂度为 \(O(n\sqrt n)\) 。

code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <set>
using namespace std;

const int max1 = 1e5;
const int inf = 0x3f3f3f3f;

int n, q, color[max1 + 5];
map < pair <int, int>, int > Map;
vector <int> edge[max1 + 5];

int siz[max1 + 5], deep[max1 + 5], father[max1 + 5], son[max1 + 5];
int top[max1 + 5], dfn[max1 + 5], rk[max1 + 5], dfs_clock;

set <int> point[max1 + 5];
int ans;

struct Part
{
    int total, len, block;
    vector <int> st, ed, bel;
    vector <int> val, tag, Min, cnt;

    void Build ()
    {
        len = sqrt(total), block = total / len;

        st.resize(total + 1), ed.resize(total + 1), bel.resize(total + 1);

        st[0] = ed[0] = 0;
        for ( int i = 1; i <= block; i ++ )
        {
            st[i] = ed[i - 1] + 1;
            ed[i] = i * len;
        }
        ed[block] = total;

        for ( int i = 1; i <= block; i ++ )
            for ( int j = st[i]; j <= ed[i]; j ++ )
                bel[j] = i;

        val.resize(total + 1);
        tag.resize(total + 1);
        Min.resize(total + 1);
        cnt.resize(total + 1);

        for ( int i = 1; i <= total; i ++ )
            val[i] = 0;
        
        for ( int i = 1; i <= block; i ++ )
        {
            tag[i] = Min[i] = 0;
            cnt[i] = ed[i] - st[i] + 1;
            ans += cnt[i];
        }
        return;
    }

    void Update ( int L, int R, int k )
    {
        if ( bel[L] == bel[R] )
        {
            int B = bel[L], BL = st[B], BR = ed[B];

            if ( !( tag[B] + Min[B] ) )
                ans -= cnt[B];

            for ( int i = BL; i <= BR; i ++ )
                val[i] += tag[B];
            for ( int i = L; i <= R; i ++ )
                val[i] += k;
            
            tag[B] = 0;
            Min[B] = inf;
            for ( int i = BL; i <= BR; i ++ )
                Min[B] = min(Min[B], val[i]);
            cnt[B] = 0;
            for ( int i = BL; i <= BR; i ++ )
                cnt[B] += val[i] == Min[B];
            
            if ( !Min[B] )
                ans += cnt[B];
        }
        else
        {
            Update(L, ed[bel[L]], k);
            Update(st[bel[R]], R, k);

            for ( int i = bel[L] + 1; i <= bel[R] - 1; i ++ )
            {
                if ( !( tag[i] + Min[i] ) )
                    ans -= cnt[i];
                
                tag[i] += k;
                
                if ( !( tag[i] + Min[i] ) )
                    ans += cnt[i];
            }
        }
        return;
    }

}part[max1 + 5];

void Find_Heavy_Edge ( int now, int fa, int depth )
{
    siz[now] = 1, deep[now] = depth, father[now] = fa, son[now] = 0;

    int max_siz = 0;

    for ( auto v : edge[now] )
    {
        if ( v == fa )
            continue;

        Find_Heavy_Edge(v, now, depth + 1);
        siz[now] += siz[v];
        if ( siz[v] > max_siz )
            max_siz = siz[v], son[now] = v;
    }
    return;
}

void Connect_Heavy_Edge ( int now, int ancestor )
{
    top[now] = ancestor;
    dfn[now] = ++dfs_clock;
    rk[dfs_clock] = now;
    ++part[top[now]].total;

    if ( son[now] )
        Connect_Heavy_Edge(son[now], ancestor);

    for ( auto v : edge[now] )
    {
        if ( v == father[now] || v == son[now] )
            continue;

        Connect_Heavy_Edge(v, v);
    }
    return;
}

int Get_Lca ( int u, int v )
{
    while ( top[u] != top[v] )
    {
        if ( deep[top[u]] < deep[top[v]] )
            swap(u, v);

        u = father[top[u]];
    }

    if ( deep[u] > deep[v] )
        swap(u, v);
    return u;
}

void Update ( int u, int v, int k )
{
    while ( top[u] != top[v] )
    {
        if ( deep[top[u]] < deep[top[v]] )
            swap(u, v);

        part[top[u]].Update(1, deep[u] - deep[top[u]] + 1, k);
        u = father[top[u]];
    }

    if ( deep[u] > deep[v] )
        swap(u, v);
    
    if ( u != v )
        part[top[u]].Update(deep[u] - deep[top[u]] + 2, deep[v] - deep[top[u]] + 1, k);
    
    return;
}

void Insert ( int x )
{
    int c = color[x];

    if ( !point[c].empty() )
    {
        int p = rk[*point[c].begin()], q = rk[*--point[c].end()], lca = Get_Lca(p, q);

        if ( dfn[x] >= dfn[lca] && dfn[x] <= dfn[lca] + siz[lca] - 1 )
        {
            p = rk[*--point[c].upper_bound(dfn[x])];
            lca = Get_Lca(p, x);

            if ( *--point[c].end() > dfn[x] )
            {
                p = rk[*point[c].upper_bound(dfn[x])];
                q = Get_Lca(p, x);

                if ( deep[q] > deep[lca] )
                    lca = q;
            }
        }

        Update(x, lca, 1);
    }

    point[c].insert(dfn[x]);

    return;
}

void Delete ( int x )
{
    int c = color[x];

    point[c].erase(point[c].find(dfn[x]));

    if ( !point[c].empty() )
    {
        int p = rk[*point[c].begin()], q = rk[*--point[c].end()], lca = Get_Lca(p, q);

        if ( dfn[x] >= dfn[lca] && dfn[x] <= dfn[lca] + siz[lca] - 1 )
        {
            p = rk[*--point[c].upper_bound(dfn[x])];
            lca = Get_Lca(p, x);

            if ( *--point[c].end() > dfn[x] )
            {
                p = rk[*point[c].upper_bound(dfn[x])];
                q = Get_Lca(p, x);

                if ( deep[q] > deep[lca] )
                    lca = q;
            }
        }

        Update(x, lca, -1);
    }

    return;
}

int main ()
{
    freopen("akee.in", "r", stdin);
    freopen("akee.out", "w", stdout);

    int tmp[3];
    scanf("%d", &n); n -= 2;
    for ( int i = 1; i <= n; i ++ )
    {
        scanf("%d%d%d%d", &tmp[0], &tmp[1], &tmp[2], &color[i]);

        sort(tmp, tmp + 3);

        for ( int j = 0; j < 3; j ++ )
        {
            for ( int k = j + 1; k < 3; k ++ )
            {
                if ( Map.find(make_pair(tmp[j], tmp[k])) != Map.end() )
                {
                    int v = Map[make_pair(tmp[j], tmp[k])];
                    edge[i].push_back(v); edge[v].push_back(i);
                }
                else
                    Map[make_pair(tmp[j], tmp[k])] = i;
            }
        }
    }

    for ( int i = 1; i <= n; i ++ )
        part[i].total = 0;

    Find_Heavy_Edge(1, 0, 0);
    Connect_Heavy_Edge(1, 1);

    for ( int i = 1; i <= n; i ++ )
        if ( i == top[i] )
            part[i].Build();

    for ( int i = 1; i <= n; i ++ )
        Insert(i);
    
    printf("%d\n", ans - 1);
    scanf("%d", &q);

    int x, p;
    while ( q -- )
    {
        scanf("%d%d", &x, &p);
        Delete(x);
        color[x] = p;
        Insert(x);
        printf("%d\n", ans - 1);
    }

    return 0;
}

T3 普及题

设第 \(i\) 个特殊点的临域集合为 \(S_i\) ,设 \(S=\cup S_i\) ,那么我们需要求解 \(|S|\) ,考虑构造一个长度为 \(\sum |S_i|\) 的序列 \(x\) ,对于每个集合 \(S_i\) ,设 \(a\) 为 \(S_i\) 中一个元素,设 \(a\) 在所有集合中的出现次数为 \(r\) ,那么 \(\tfrac{1}{r}\) 位于序列 \(x\) 中,容易发现 \(\sum x_i\) 就是我们需要求解的答案。

由于 \(x\) 序列的长度很容易计算,因此考虑求解 \(E(x_i)\) ,一种经典做法是随机取出 \(x\) 序列中一个数,然后求解所有数的平均值。

首先考虑随机的过程,选定当前数属于的集合(假设当前随机次数确定,可以直接计算当前集合内期望取出的数的个数),然后等概率随机集合内一个与当前集合对应点存在连边的点,由于可以确定当前集合的大小,可以随机排名后,通过字典序按位依次确定,设随机次数为 \(N\) ,此时复杂度为 \(O(Nk)\) 。

考虑求解当前随机点所对应的 \(r\) ,直接暴力的复杂度为 \(O(Tk)\) ,总复杂度为 \(O(NTk^2)\) ,然而题解表示 \(N\) 的大小需要为 \(O(k^2\epsilon^2)\) ,即使时限为 \(20s\) 也无法通过,考虑优化这个过程,预处理数组 \(f_{i,j,w}\) 表示在 \(i\) 这一维(一个点总共有 \(k\) 维),原图上 \(j\) 点与第 \(w\) 个特殊点的第 \(i\) 维是否存在连边,由于数组值只存在 \(0/1\) 状态,因此可以压位处理,考虑当前随机点与哪些特殊点存在连边,容易直接得到当前随机点的每一维与特殊点在这一维上的连边情况,但是由于这个矩阵是竖着压位存储的,而我们计算的信息(当前随机点与特殊点每一维上边的个数)需要这个矩阵横着压位存储才能使用 __builtin_popcount 快速计算,因此考虑对原矩阵进行转置。

考虑一种转置的方法,假设当前矩阵的行列相等并且均为 \(2\) 的整数幂,可以考虑分治,将原矩阵行列分成均等的四份,对四个小矩阵分别转置,之后交换左下角与右上角的矩阵,容易发现此时的矩阵就是转置矩阵,复杂度为 \(O(n^2\log n)\) ,由于矩阵每一位只存在 \(0/1\) 状态,因此这个过程可以使用神奇的二进制技巧实现,复杂度可以做到 \(O(\tfrac{n^2\log n}{w})\) ,可以通过本题。

code
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <random>
#include <bitset>
#include <iostream>
using namespace std;

const int max1 = 32, max2 = 1000;
const int seed = 1204;
mt19937 rnd((unsigned long long)&seed);

double Sdouble ( double L, double R )
{
    return uniform_real_distribution <double> (L, R) (rnd);
}

int n, m, k, s, T;
vector <int> edge[max2 + 5], rev_edge[max2 + 5];
bitset <max2 + 5> connect[max2 + 5];
int point[max1 + 5][max1 + 5];
unsigned int is_edge[max1 + 5][max2 + 5];
unsigned int tmp[max1 + 5], matrix[max1 + 5];

double f[max1 + 5][max1 + 5], sum[max1 + 5], total, ans;

int sp[max1 + 5], cnt;

int main ()
{
    freopen("tech.in", "r", stdin);
    freopen("tech.out", "w", stdout);

    scanf("%d%d%d%d%d", &n, &m, &k, &s, &T);

    for ( int i = 1, u, v; i <= m; i ++ )
    {
        scanf("%d%d", &u, &v);
        edge[u].push_back(v);
        edge[v].push_back(u);

        connect[u][v] = connect[v][u] = 1;
    }

    for ( int i = 1; i <= T; i ++ )
        for ( int j = 1; j <= k; j ++ )
            scanf("%d", &point[i][j]);
    
    for ( int i = 1; i <= n; i ++ )
    {
        sort(edge[i].begin(), edge[i].end());
        edge[i].erase(unique(edge[i].begin(), edge[i].end()), edge[i].end());
        int pre = 0;
        for ( auto v : edge[i] )
        {
            for ( int j = pre + 1; j <= v - 1; j ++ )
                rev_edge[i].push_back(j);
            pre = v;
        }

        for ( int j = pre + 1; j <= n; j ++ )
            rev_edge[i].push_back(j);
    }

    for ( int i = 1; i <= T; i ++ )
    {
        for ( int j = 1; j <= k; j ++ )
        {
            for ( auto v : edge[point[i][j]] )
            {
                is_edge[j][v] |= 1u << i - 1;
            }
        }
    }
    
    for ( int i = 1; i <= T; i ++ )
    {
        memset(f, 0, sizeof(f));
        f[0][0] = 1.0;
        for ( int j = 1; j <= k; j ++ )
        {
            for ( int w = 0; w <= s; w ++ )
            {
                f[j][w] += f[j - 1][w] * rev_edge[point[i][j]].size();
                f[j][w + 1] += f[j - 1][w] * edge[point[i][j]].size();
            }
        }
        sum[i] = f[k][s];
        total += sum[i];
    }

    if ( total < 1 )
    {
        printf("0\n");
        return 0;
    } 

    for ( int i = 1; i <= T; i ++ )
    {
        int p = k * k * 10000 * sum[i] / total;

        memset(f, 0, sizeof(f));
        f[0][0] = 1.0;
        for ( int j = 1; j <= k; j ++ )
        {
            for ( int w = 0; w <= s; w ++ )
            {
                f[j][w] += f[j - 1][w] * rev_edge[point[i][j]].size();
                f[j][w + 1] += f[j - 1][w] * edge[point[i][j]].size();
            }
        }

        while ( p -- )
        {
            double val = floor(Sdouble(1.0, sum[i] + 0.99999));
            int now = s;

            bool flag = false;

            for ( int j = k; j >= 1; j -- )
            {
                if ( f[j - 1][now] * rev_edge[point[i][j]].size() >= val )
                {
                    sp[j] = rev_edge[point[i][j]][(int)( ( val - 1 ) / f[j - 1][now] )];
                    val -= (int)( ( val - 1 ) / f[j - 1][now] ) * f[j - 1][now];
                }
                else
                {
                    if ( !now )
                        { flag = true; break; }
                    
                    val -= f[j - 1][now] * rev_edge[point[i][j]].size();
                    sp[j] = edge[point[i][j]][(int)( ( val - 1 ) / f[j - 1][now - 1] )];
                    val -= (int)( ( val - 1 ) / f[j - 1][now - 1] ) * f[j - 1][now - 1];
                    --now;
                }
            }

            if ( flag )
                continue;

            for ( int j = 1; j <= 32; j ++ )
                matrix[j - 1] = is_edge[j][sp[j]];
            
            for ( int bit = 0; bit < 5; bit ++ )
            {
                for ( int j = 0; j < 32; j ++ )
                    tmp[j] = matrix[j];
                
                unsigned int x = 0, y = 0;
                for ( int j = 0; j < 32; j ++ )
                {
                    if ( j >> bit & 1 )
                        x |= 1u << j;
                    else
                        y |= 1u << j;
                }

                for ( int j = 0; j < 32; j += 1 << bit + 1 )
                {
                    for ( int w = 0; w < 1 << bit; w ++ )
                    {
                        matrix[j + w] = ( ( tmp[j + w] & y ) | ( tmp[j + ( 1 << bit ) + w] & y ) << ( 1 << bit ) );
                        matrix[j + ( 1 << bit ) + w] = ( ( tmp[j + w] & x ) >> ( 1 << bit ) | ( tmp[j + ( 1 << bit ) + w] & x ) );
                    }
                }
            }

            int match = 0;
            for ( int j = 0; j < 32; j ++ )
                match += __builtin_popcount(matrix[j]) == s;
            if ( match )
            {
                ans += 1.0 / match;
                ++cnt;
            }
        }
    }

    ans = ans * total / cnt;
    printf("%.8lf\n", ans);

    return 0;
}

标签:15.1,point,int,top,max1,国赛,deep,2023,now
From: https://www.cnblogs.com/KafuuChinocpp/p/17472102.html

相关文章

  • 202306-人民当家作组 实验七 综合软件项目案例
    项目内容课程班级博客链接2020级卓越工程师班这个作业要求链接实验七综合软件项目案例团队名称人民当家作组团队的课程学习目标(1)练习用例图、类图、顺序图、状态图等UML建模技术在软件开发过程中的用途;(2)掌握软件项目的数据库逻辑结构设计方法;(3)掌握软件项目......
  • 202307-什么是快乐星球组 实验七:综合软件项目案例
    项目内容课程班级博客链接2020级计算机科学与技术本次作业要求链接实验七:综合软件项目案例团队名称什么是快乐星球组团队成员分工描述张倩:任务三、任务四、任务六贾小萌:任务二、任务六、撰写博客葛薇:任务一、任务五、任务七、任务八团队的课程学习目标......
  • 江苏省2023年普通高校招生全国统一考试游记
    前言好久没写博客了呢,这一个月一来也发生了很多事吧不过因为我的偷懒,很多有趣的事情都没有记录下来然后游记也不是考完就写的,也算是一个回忆吧Day1其实一开始我对自己的要求也不高,把试卷填满就行所以说上考场之前应该还算比较轻松吧然后第一天老师们送考的服装是红色的代......
  • 2023/6/10 学习笔记
    欧拉图欧拉图的定义欧拉回路:所有的边都经历一次不重复的回路欧拉通路:所有的边都经历一次不重复的路径欧拉图:具有欧拉回路的图半欧拉图:具有欧拉通路的图 连通图只有0个或者偶数个奇数出度点判别方法:1.无向图欧拉回路:(1)除去度为0的点外,其他的点相互连通(2)顶点度数......
  • (2023.6.10)线程绑定到指定核上
    pthread_setaffinity_np与sched_setaffinity的区别:sched_setaffinity可在进程的线程中去修改亲和性写在启动脚本中是使用pthread_setaffinity_np、sched_setaffinity、还是tasklet?(https://www.cnblogs.com/x_wukong/p/5924298.html)c语言如何调用到系统命令reboot? 同时在......
  • 202303-天天向上队 实验七 综合软件项目案例
    项目内容课程班级博客链接2023年春软件工程这个作业要求链接实验七综合软件项目案例团队名称天天向上队团队的课程学习目标(1)练习用例图、类图、顺序图、状态图等UML建模技术在软件开发过程中的用途。(2)掌握软件项目的数据库逻辑结构设计方法。(3)掌握软件项目......
  • 2023-06-10:给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示 在节点网络
    2023-06-10:给定一个由n个节点组成的网络,用nxn个邻接矩阵graph表示在节点网络中,只有当graph[i][j]=1时,节点i能够直接连接到另一个节点j。一些节点initial最初被恶意软件感染。只要两个节点直接连接,且其中至少一个节点受到恶意软件的感染,那么两个节点都将被恶意软件......
  • 2023Fiddler抓包学习笔记 -- 环境配置及工具栏介绍
    一、Fiddler介绍Fiddler是位于客户端和服务器端的HTTP代理,常用来抓http数据包,可以监控浏览器所有的http和https流量,查看分析请求数据包和响应数据包,伪造请求和响应等功能。二、下载安装1、下载地址https://www.telerik.com/download/fiddler/fiddler42、一路下一步安装,安装完成后,发......
  • 2023-06-10:给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示 在节点网络
    2023-06-10:给定一个由n个节点组成的网络,用nxn个邻接矩阵graph表示在节点网络中,只有当graph[i][j]=1时,节点i能够直接连接到另一个节点j。一些节点initial最初被恶意软件感染。只要两个节点直接连接,且其中至少一个节点受到恶意软件的感染,那么两个节点都将被恶意......
  • SummerResearch_Log_20230610
    WorkingContent:1.目前要做的任务是将classifier_resnet18.py用的方法做一些改动,原来是训练一个被污染的数据集,然后用干净的测试集去测试正常数据的识别成功率和污染数据的攻击成功率。比如某种dog属于dog类,我现在找了个trigger(比如加了个黑方格到dog的图像上),并且把加了trigg......