• 10.04 FZSZ模拟Day1 总结


    今天轮到FZSZ出题了,这可是连续两年捧杯NOI的学校了……

    可想而知今天题难度有多大……不过似乎还要庆幸出题的是一位叫Anzhe Wang 的大神而不是fjzzq?

    T1.permutation

    期望得分40,实际得分40.

    这道题看起来很像是组合题……想起昨天的组合题,想试试能不能用类似的做法去做。后来发现不可行,不可递推,因为昨天的题其实还很良心,只是相邻两个元素之间会互相影响,而这个题前面的元素会影响到后面的元素,所以难以递推。

    我大概开场想了30min没思路 然后就去看后面了……

    回来大概一想想到这题40pts可以用状压DP水过,就是某一位上是i表示那一位当前是被选取状态,这样其实我们是可以通过转移来确定先后顺序的。这样复杂度是O(2^n * n)的,可以过40pts。

    然后看了题解……题解真是让人脑洞大开……其实我们发现这个选取的情况可以对应一个二分图。我们把所有的pi放在一个点集,所有的i(也可以理解为位置)放在一个点集。

    如果我们令二分图中的连边表示实际匹配时的不合法情况,我们会得到以下的图。

     

    也就是说,这个二分图是由许多条不相交的链组成的(所谓的不相交是指没有属于不同链的两条边连在一个点上),那么我们要求的其实就是这个二分图的补图的匹配方案数。(补图简单的定义就是,原二分图中有的边它没有,原来没有的边他有。)

    我们令g[i]表示在这个二分图中选取i条边的方案数(也就是等于确定了i个不合法的情况)

    那么我们得到答案有如下式子:

     来解释一下,首先(n-i)!表示一个全排列,就是因为你当前确定有i条边是不合法的,那么剩余的n-i条边就是自由排列的,那就是它的阶乘次的方案数。后面的是什么意思呢?首先我们知道如果啥都不管的话,那么n!是所有的情况,但是你在n!种的情况之中,必然会统计到有一条边是不合法的情况,于是乎我们就要去计算有一条边不合法的情况,然后把它减掉。但是在这样计算的时候又会多把有两条边不合法的情况减掉,相当于多减了,然后我们还得给加回来……所以这样层层递推就有了最后的式子。

    然后我们去求g[i],用f[i][j][0/1]表示在一条链上取到第i个点,取了j条链,当前点取或者没取的情况数,那么就有转移:

    f[i][j][0] = f[i-1][j][1] + f[i-1][j][0];

    f[i][j][1] = f[i-1][j-1][0];

    最后我们直接用f把g合并出来就可以了。时间复杂度是O(n^2/k + n)?能过95.最后一个点什么NTT真的不会……

    然而这个也不想写……

    40pts状压代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<utility>
    #include<map>
    #define pr pair<int,int>
    #define mp make_pair
    #define fi first
    #define sc second
    #define lowbit(x) x & (-x)
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    const int M = 2000005;
    const int N = 10000005;
    const ll mod = 998244353;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >='0' && ch <= '9')
        {
            ans *= 10;
            ans += ch - '0';
            ch = getchar();
        }
        return ans * op;
    }
    
    int n,k,cur;
    ll dp[M];
    
    int getsum(int x)
    {
        int s = 0;
        while(x) s++,x -= lowbit(x);
        return s;
    }
    
    int main()
    {
        freopen("permutation.in","r",stdin);
        freopen("permutation.out","w",stdout);
        n = read(),k = read();
        rep(i,0,n-1) if(i != k) dp[1<<i] = 1;
        rep(i,1,(1<<n)-1)
        {
            int h = getsum(i);
            rep(j,0,n-1)
            {
                if(i & (1 << j) || (abs(j-h) == k)) continue;
                dp[i | (1 << j)] += dp[i],dp[i | (1 << j)] %= mod;
            }
        }
        printf("%lld
    ",dp[(1<<n)-1]);
        return 0;
    }

    看一下学姐的代码。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <ctime>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define mp make_pair
    #define enter putchar('
    ')
    #define space putchar(' ') 
    //#define ivorysi
    #define MAXN 100005
    typedef long long int64;
    using namespace std;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
            if(c == '-') f = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') {
            res = res * 10 + c - '0';
            c = getchar();
        }
        res *= f;
    }
    template<class T> 
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
            out(x / 10);
        }
        putchar('0' + x % 10);
    }
    const int MOD = 998244353;
    int inc(int a,int b) {
        return a + b >= MOD ? a + b - MOD : a + b;
    }
    int mul(int a,int b) {
        return 1LL * a * b % MOD;
    }
    void update(int &x,int y) {
        x = inc(x,y);
    }
    int fac[MAXN],N,K,g[MAXN],f[2005 * 2][2005][2];
    bool vis[2][MAXN];
    void DP(int st,int cnt) {
        for(int j = 0 ; j <= N ; ++j) update(f[st + 1][j][0],inc(f[st][j][0],f[st][j][1]));
        for(int h = st + 2 ; h <= st + cnt ; ++h) {
            for(int j = 0 ; j <= N ; ++j) {
                update(f[h][j][0],inc(f[h - 1][j][1],f[h - 1][j][0]));
                if(j >= 1) update(f[h][j][1],f[h - 1][j - 1][0]);
            }
        }
    }
    void Solve() {
        read(N);read(K);
        fac[0] = 1;
        for(int i = 1 ; i <= N ; ++i) fac[i] = mul(fac[i - 1],i);
        int tot = 0;
        memset(vis,0,sizeof(vis));
        f[0][0][0] = 1;
        for(int i = 1 ; i <= N ; ++i) {
            if(!vis[0][i]) {
                int cnt = 0;
                for(int j = i ; j <= N ; j += 2 * K) {
                    vis[0][j] = 1;
                    cnt++;
                    if(j + K <= N) {vis[1][j + K] = 1;++cnt;}
                }
                DP(tot,cnt);
                tot += cnt;
            }
            if(!vis[1][i]) {
                int cnt = 0;
                for(int j = i ; j <= N ; j += 2 * K) {
                    vis[1][j] = 1;
                    ++cnt;
                    if(j + K <= N) {
                        vis[0][j + K] = 1;++cnt;
                    }
                }
                DP(tot,cnt);
                tot += cnt;
            }
        }
        for(int i = 0 ; i <= N ; ++i) {
            g[i] = inc(f[2 * N][i][0],f[2 * N][i][1]);
        }
        int t = 1,ans = 0;
        for(int i = 0 ; i <= N ; ++i) {
            update(ans,mul(t,mul(g[i],fac[N - i])));
            t = mul(t,MOD - 1);
        }
        out(ans);enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #else
        freopen("permutation.in","r",stdin);
        freopen("permutation.out","w",stdout);
    #endif
        Solve();
        return 0;
    }

    T2.tree

    期望得分30,实际得分40

    这题大概看了20多分钟,没什么思路,但是可以疯狂爆搜,暴力枚举哪两条边要删除,然后暴力dfs判断图是否联通,复杂度O(n^3),得分40.

    这道题的60pts做法是枚举每一条原来的树边,之后对新图跑他让tarjan去求桥,每座非树边的桥有1的贡献。

    然后满分的做法就是,我们发现对于一条树边,一棵子树内的所有节点如果有两条或者以上连了出去,那么你怎么割也无法割断,如果只有一条,那么就有1的贡献,如果没有,那就随便找一条割断,也就是有m的贡献。

    所以我们可以进行树上差分。对于每一条新加的边,我们把它拆成从一个点到LCA和另一个点到LCA的两条路径,分别差分维护。最后我们统计一下,每个点的点权为1则有1的贡献,为0有m的贡献。

    我写的时候是用树剖的,也能过300000.

    然后学姐还有更强的操作,直接维护dfs序,统计一个子树管辖的区间之内能向左/右延伸的最远的两条边能延伸到的范围,最后统计的时候如果有两天或以上的边,你就割不断,有一条贡献为1,0条则贡献为m。(%学姐orz)

    40pts爆搜不看了,直接上100的树剖。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<utility>
    #include<map>
    #define pr pair<int,int>
    #define mp make_pair
    #define fi first
    #define sc second
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    const int M = 300005;
    const int N = 10000005;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            ans *= 10;
            ans += ch - '0';
            ch = getchar();
        }
        return ans * op;
    }
    
    struct edge
    {
        int next,to;
    }e[M<<2];
    
    struct seg
    {
        int v,lazy;
    }t[M<<2];
    
    int n,m,dfn[M],head[M],ecnt,size[M],fa[M],dep[M],top[M],hson[M],idx,ch[M],x,y;
    ll ans;
    
    void add(int x,int y)
    {
        e[++ecnt].to = y;
        e[ecnt].next = head[x];
        head[x] = ecnt;
    }
    
    void dfs1(int x,int f,int depth)
    {
        fa[x] = f,dep[x] = depth,size[x] = 1;
        int maxson = -1;
        for(int i = head[x];i;i = e[i].next)
        {
            if(e[i].to == f) continue;
            dfs1(e[i].to,x,depth+1);
            size[x] += size[e[i].to];
            if(size[e[i].to] > maxson) maxson = size[e[i].to],hson[x] = e[i].to;
        }
    }
    
    void dfs2(int x,int t)
    {
        top[x] = t,dfn[x] = ++idx;
        if(!hson[x]) return;
        dfs2(hson[x],t);
        for(int i = head[x];i;i = e[i].next)
        {
            if(e[i].to == fa[x] || e[i].to == hson[x]) continue;
            dfs2(e[i].to,e[i].to);
        }
    }
    
    void pushdown(int p,int l,int r)
    {
        int mid = (l+r) >> 1;
        t[p<<1].lazy += t[p].lazy,t[p<<1|1].lazy += t[p].lazy;
        t[p<<1].v += t[p].lazy * (mid-l+1),t[p<<1|1].v += t[p].lazy * (r-mid);
        t[p].lazy = 0;
    }
    
    void modify(int p,int l,int r,int kl,int kr)
    {
        if(l == kl && r == kr) 
        {
            t[p].v += (r-l+1),t[p].lazy++;
            return;
        }
        int mid = (l+r) >> 1;
        if(t[p].lazy) pushdown(p,l,r);
        if(kr <= mid) modify(p<<1,l,mid,kl,kr);
        else if(kl > mid) modify(p<<1|1,mid+1,r,kl,kr);
        else modify(p<<1,l,mid,kl,mid),modify(p<<1|1,mid+1,r,mid+1,kr);
    }
    
    int query(int p,int l,int r,int pos)
    {
        if(l == r) return t[p].v;
        int mid = (l+r) >> 1;
        if(t[p].lazy) pushdown(p,l,r);
        if(pos <= mid) return query(p<<1,l,mid,pos);
        else return query(p<<1|1,mid+1,r,pos);
    }
    
    void mrange(int x,int y)
    {
        while(top[x] != top[y])
        {
            if(dep[top[x]] < dep[top[y]]) swap(x,y);
            modify(1,1,n,dfn[top[x]],dfn[x]);
            x = fa[top[x]];
        }
        if(dep[x] > dep[y]) swap(x,y);
        if(dfn[x] + 1 > dfn[y]) return;
        modify(1,1,n,dfn[x]+1,dfn[y]);
    }
    
    int main()
    {
    //    freopen("tree.in","r",stdin);
    //    freopen("tree.out","w",stdout);
        n = read(),m = read();
        rep(i,1,n-1) x = read(),y = read(),add(x,y),add(y,x);
        dfs1(1,0,1),dfs2(1,1);
        rep(i,1,m) x = read(),y = read(),mrange(x,y);
        rep(i,1,n) ans += (query(1,1,n,dfn[i]) == 1);
        rep(i,2,n) ans += (query(1,1,n,dfn[i]) == 0) * m;
        printf("%lld
    ",ans);
        return 0;
    }

    T3.polynomial

    期望得分30,实际得分0.

    这道题是真心不可做……题解里面什么FFT是搞哪样……

    考试的时候暴力打表n<=4的情况,结果发现自己推4个一样的时候推错了,多推了3个,爆零。

    还是放上改好的暴力打表30吧……正解代码11kb又是哪样……

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<utility>
    #include<map>
    #define pr pair<int,int>
    #define mp make_pair
    #define fi first
    #define sc second
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    const int M = 100005;
    const int N = 10000005;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >='0' && ch <= '9')
        {
            ans *= 10;
            ans += ch - '0';
            ch = getchar();
        }
        return ans * op;
    }
    
    struct card
    {
        int num,col;
    }c[50];
    
    int T,n,posa,posb,cur;
    char s[5];
    bool jok;
    
    void clear()
    {
        memset(c,0,sizeof(c));
        jok = 0,cur = posa = posb = 0;
    }
    
    bool same()
    {
        return (c[1].num == c[2].num) && (c[2].num == c[3].num) && (c[3].num == c[4].num);
    }
    
    bool ssame()
    {
        rep(i,1,n)
        {
            rep(j,i+1,n)
            rep(k,j+1,n)
            if((c[i].num == c[j].num) && (c[j].num == c[k].num)) return 1;
        }
        return 0;
    }
    
    bool tsame()
    {
        rep(i,1,n)
        {
            rep(j,i+1,n)
            if(c[i].num == c[j].num) 
            {
                posa = i,posb = j;
                return 1;
            }
        }
        return 0;
    }
    
    void naive()
    {
        if(n == 1) 
        {
            printf("1
    ");
            return;
        }
        if(n == 2)
        {
            if(c[1].num == c[2].num) printf("2
    ");
            else if(c[1].num + c[2].num == 29) printf("2
    ");
            else printf("1
    ");
            return;
        }
        if(n == 3)
        {
            if(c[1].num == c[2].num && c[2].num == c[3].num) printf("5
    ");
            else 
            {
                rep(i,1,n)
                rep(j,i+1,n) 
                {
                    if(c[i].num == c[j].num || c[i].num + c[j].num == 29)
                    {
                        printf("2
    ");
                        return;    
                    }
                }
                printf("1
    ");
                return;
            }
        }
        rep(i,1,n) 
        {
            rep(j,i+1,n) 
            if(c[i].num + c[j].num == 29) jok = 1;
        }
        if(jok)
        {
            rep(i,1,n)
            rep(j,i+1,n) 
            {
                if(c[i].num == c[j].num)
                {
                    printf("4
    ");
                    return;
                }
            }
            printf("2
    ");
            return;
        }
        if(same())
        {
            printf("15
    "); 
            return;
        }
        if(ssame())
        {
            printf("6
    ");
            return ;
        }
        if(tsame())
        {
            cur = 0;
            rep(i,1,n)
            {
                if(i == posa || i == posb) continue;
                if(cur == c[i].num) 
                {
                    printf("4
    ");
                    return;
                }
                if(!cur) cur = c[i].num;
            }
            printf("2
    ");
            return;
        }
        printf("1
    ");
        return;
    }
    
    int main()
    {
        freopen("polynomial.in","r",stdin);
        freopen("polynomial.out","w",stdout);
        srand(19260817);
        T = read();
        while(T--)
        {
            clear();
            n = read();
            rep(i,1,n) 
            {
                scanf("%s",s);
                if(s[0] >= '2' && s[0] <= '9') c[i].num = s[0] - '0';
                else if(s[0] < '0' || s[0] > '9') c[i].num = s[0] - 'A' + 1;
                else c[i].num = 10 + s[1] - '0';
            }
            rep(i,1,n) c[i].col = read();
            if(n <= 4) naive();
            else printf("%d
    ",rand());
        }
        return 0;
    }

    感觉自己还是太弱,不知道明天能咋样orz。

  • 相关阅读:
    如何打开指定文件所在的文件夹并选中文件
    不阻止多线程中控件跨线程访问
    .NET使用并行计算 提高执行效率
    关于线程安全中Lock的一些说明
    多线程中调用多参数的方法
    C#把汉字转换成拼音
    跨窗体Invoke时使用匿名方法或者带参方法
    给MP3音乐文件写ID3信息和专辑封面
    用Python作GIS:菜谱篇
    python中对象self的由来
  • 原文地址:https://www.cnblogs.com/captain1/p/9743367.html
Copyright © 2020-2023  润新知