• GRYZ20211103模拟赛解题报告


    期望得分:(100+100+100=300pts)
    实际得分:(100+100+0=200pts)

    恭喜我自己,没有算空间挂了 100pts。

    7:40 发题面
    7:48 读完题,发现 T1 和二进制有关
    7:56 调完 T1。T2 期望感觉巨难直接开 T3,想了想 T3 的做法稍微算了一下复杂度,感觉正确性没什么问题,复杂度可能有点卡,然后开写。
    8:45 调了好久终于过了样例,然后一发过了大样例,只不过跑的有点慢,算了一下发现复杂度果然过不去,对一些地方进行了优化,极限数据本地跑了 7s,应该刚好能卡过去的程度。可能会爆 long long 就把 define int long long 写上了。(埋下伏笔)
    9:10 上厕所,回来开始想 T2,推了推感觉 k=1 的情况挺好写,就把这一部分写了,现在对于正解还是没有思路的。
    9:35 想起来答应给 @斜揽残箫 写的题解还没写,于是先把他的题解写完了。
    10:00 有上了次厕所,回来看了看 T2,转化了一下思路,感觉可以做,手推了式子开始写。
    10:35 终于调出来了,有惊无险(一发过大样例还是挺不错的)。检查了一下,把这个题解写了,然后开始摆烂,拿出了 BS 给的小说。
    10:55 收卷。果然自己被卡空间了,wsdsb。/hanx

    感觉 T2 最后能想出来非常的幸运,不过 T3 没算空间就比较傻逼了。

    为什么我老挂分啊/ll,为什么我老挂分啊/ll,为什么我老挂分啊/ll,为什么我老挂分啊/ll,为什么我老挂分啊/ll,为什么我老挂分啊/ll,为什么我老挂分啊/ll,为什么我老挂分啊/ll

    T3 最后在评测机和洛谷上测了测,有两个点是踩着 2000ms 的线卡过去的。

    T1 T2 T3
    T208866 city T208867 paint T208869 vanilla

    T1 city

    如果把数转化成二进制再来看这三个操作就非常简单了。

    分别是左移一位+1,左移一位,右移一位。

    所以每次操作只能更改两个数的低位,那我们看看这两个数从高位开始匹配能匹配多少位,答案就是两个数剩下的不能匹配的位数和。

    int main()
    {
    //    freopen("city.in","r",stdin);
    //    freopen("city.out","w",stdout);
    	n = read();
    	while(n--) {
    	    x = read(), y = read(), sc = 0, top = 0;
    	    while(x) a[++sc] = (x & 1), x >>= 1; 
            while(y) b[++top] = (y & 1), y >>= 1;
            while(sc && top && a[sc] == b[top]) sc--, top--;
            printf("%d
    ", sc + top);
        }
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    

    T2 paint

    如果你按照题意去模拟会非常难搞,考虑转化一下思路。

    首先,一张画纸期望是什么颜色只与它被修改几次有关。因此我们可以设 (f_{i,j}) 表示一张画纸被修改了 (j) 次变成了 (i) 的期望。

    然后我们考虑一下区间覆盖的问题,如果一次作画框住了区间 ([l,r])。设这个区间长度为 (len),我们考虑其中的某个位置会被选中多少次,那就是假设它被选中其他的位置随便选的方案数,所以它会被选中 (2^{len-1}),而整个区间的子集有 (2^{len}) 种,说的可能麻烦了,但不难发现,一个位置被选中的概率为 (frac{1}{2})

    所以呢,我们就可以利用差分快速统计出每个位置被区间覆盖了多少次。

    然后开始统计答案!

    我们枚举每个位置,枚举它可能被覆盖了几次,计算出这个次数,计算它期望的颜色,对所有的期望的颜色求和就是答案。

    那写成公式就是:

    [sum_{i=1}^{n} sum_{k=0}^{d} frac{1}{2^d} inom{d}{k} sum_{j = 0}^{c - 1} j imes f_{j, k} ]

    其中 (i) 表示第 (i) 个位置,(k) 表示枚举到被覆盖了 (k) 次,(d) 表示一共被 (d) 个区间覆盖,(j) 表示期望为颜色 (j) 的概率。

    时间复杂度为 (mathcal O(Kc^2 + nK(K+c)))

    /*
    Work by: Suzt_ilymtics
    Problem: 不知名屑题
    Knowledge: 垃圾算法
    Time: O(能过)
    
    Believe yourself ! 
    
    OHHHHHHHHHHHHHHHHHHH一发过大样例! 
    
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #define LL long long
    #define LD long double 
    #define orz cout<<"lkp AK IOI!"<<endl
    
    const int MAXN = 10005;
    using namespace std;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n, c, K;
    LD ans = 0.0;
    int L[MAXN], R[MAXN];
    int cnt[MAXN];
    LD f[200][200];
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    namespace Subtask1 {
        void Solve() {
            int len = R[1] - L[1] + 1, lst = n - len;
            printf("%.3lf
    ", lst + 1.0 * len / 2 + 1.0 * len * (c - 1) / 4);
            return ;
        }
    }
    
    void Init() {
        f[1][0] = 1.0;
        for(int j = 0; j <= K; ++j) {
            for(int i = 0; i < c; ++i) {
                for(int k = 0; k < c; ++k) {
                    f[i * k % c][j + 1] += f[i][j] / c;
                }
            }
        }
    }
    
    int main()
    {
    //    freopen("paint.in","r",stdin);
    //    freopen("paint.out","w",stdout);
    	n = read(), c = read(), K = read();
    	Init();
    	for(int i = 1; i <= K; ++i) L[i] = read(), R[i] = read();
    //	if(K == 1) return Subtask1::Solve(), 0;
    	for(int i = 1; i <= K; ++i) cnt[L[i]]++, cnt[R[i] + 1] --;
        for(int i = 1; i <= n; ++i) cnt[i] = cnt[i - 1] + cnt[i];
        for(int i = 1; i <= n; ++i) {
            LD p = 1.0;
            for(int j = 1; j <= cnt[i]; ++j) p = 1.0 * p * j / 2;
            for(int j = 0; j <= cnt[i]; ++j) {
                LD res = p, x = 0;
                for(int k = 0; k < c; ++k) x = x + f[k][j] * k;
                for(int k = 1; k <= j; ++k) res = 1.0 * res / k;
                for(int k = 1; k <= cnt[i] - j; ++k) res = 1.0 * res / k;
                res *= x;
    //            cout<<res<<"
    ";
                ans = ans + res;
            }
        }
        printf("%.3Lf", ans);
        return 0;
    }
    

    T3 vanilla

    你看这个数据范围就知道要状压,你看它要求最短路径就得状压最短路。

    我们先跑个 Folyd 求出两两之间的最短距离。

    我们在设两个数组 (f[S][i], g[S][i]) 分别表示以 (1) 作为出发点,已经进行收割/播种的状态为 (S),最后一个点为 (i) 的最短距离,(g) 数组则表示以 (n) 为出发点。

    上面这个 (f,g) 数组可以预处理出来。时间复杂度为 (mathcal O(2^n n^2)),这个复杂度有点危,我们发现我们不用枚举全部的,我们只需要枚举 (S)(1) 的个数 (le frac{n-2}{2}+2) 的状态即可。复杂度就被我们优化掉一个 (n)(我们本质还是枚举所有子集,只不过在某些子集只做了 (mathcal O(n)) Check )

    然后你枚举首先选择哪 (frac{n-2}{2}) 个花田收割,设枚举的收割的花田的状态为 (S)

    我们考虑收割的过程,考虑怎么把前一半的最短路和后一半的最短路合并起来,那就是,枚举在集合中的点 (i) 和不在集合中的点 (j),设答案为 (res),则:

    [res = min { f[S|1][i] + g[M^(S|(1<<n-1))][j] + dis[i][j] } ]

    其中 (M) 表示所有点的全集。

    这样就保证了 (S) 状态中选中的点一定会在前 (frac{n-2}{2}) 花田里收割,剩余的点在后来收割。

    同理,我们考虑播种的过程,其实就是反过来,我们再设一个播种的答案 (ans),则

    [ans = min { g[S|1][i] + f[M^(S|(1<<n-1))][j] + dis[i][j] } ]

    最终答案就是 (min {res + ans})

    这个直接上搜索就行,控制一下搜索上限,可以控制复杂度为 (mathcal O(2^{frac{n-2}{2}} frac{n-2}{2}^{2}))

    在本地跑了 7s,但 XP 的拉是众所周知的,所以应该是没问题的。

    除非正解有一个更牛逼的做法。

    /*
    Work by: Suzt_ilymtics
    Problem: 不知名屑题
    Knowledge: 垃圾算法
    Time: O(能过)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #define LL long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 2e6+50;
    const int INF = 0x3f3f3f3f3f3f3f3fll;
    const int mod = 1e9+7;
    
    int n, m, N, M, ans = INF;
    int dis[22][22];
    int f[MAXN][22], g[MAXN][22];
    int a[22], sc = 0;
    int b[22], top = 0;
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    void dfs(int S, int pos, int cnt) {
        if(cnt > N) return ;
        if(pos == n) {
            if(cnt != N) return ;
            int res1 = INF, res2 = INF;
    //        cout<<S<<"
    ";
            for(int i = 1; i <= sc; ++i) {
                for(int j = 1; j <= top; ++j) {
                    int x = a[i], y = b[j];
                    res1 = min(res1, f[S | 1][x] + g[M ^ (S | 1)][y] + dis[x][y]);
                    res2 = min(res2, g[S | (1 << n - 1)][x] + f[M ^ (S | (1 << n - 1))][y] + dis[x][y]);
                }
            }
    //        cout<<res1<<" "<<res2<<"
    ";
            ans = min(ans, res1 + res2);
            return ;
        }
        a[++sc] = pos;
        dfs(S | (1 << pos - 1), pos + 1, cnt + 1);
        --sc;
        b[++top] = pos;
        dfs(S, pos + 1, cnt);
        --top;
    }
    
    signed main()
    {
    //    freopen("vanilla.in","r",stdin);
    //    freopen("vanilla.out","w",stdout);
    	n = read(), m = read();
    	if(n <= 2) return puts("0"), 0;
    	N = (n - 2) / 2, M = (1 << n) - 1;
    	memset(dis, 0x3f, sizeof dis);
    	for(int i = 1; i <= n; ++i) dis[i][i] = 0;
    	for(int i = 1, u, v, w; i <= m; ++i) {
    	    u = read() + 1, v = read() + 1, w = read();
    	    dis[u][v] = dis[v][u] = min(dis[u][v], w);
        }
        for(int k = 1; k <= n; ++k) {
            for(int i = 1; i <= n; ++i) {
                for(int j = 1; j <= n; ++j) {
                    dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
                }
            }
        }
        if(n == 3) {
            cout<<1ll * (dis[1][2] + dis[2][3]) * 2<<"
    ";
            return 0; 
        }
        memset(f, 0x3f, sizeof f);
        f[1][1] = 0;
        for(int S = 0; S < (1 << n); ++S) {
            int cnt = 0;
            for(int i = 1; i <= n; ++i) if(S & (1 << i - 1)) cnt++;
            if(cnt > N + 2) continue;
            for(int i = 1; i <= n; ++i) {
                if(!(S & (1 << i - 1))) continue;
                for(int j = 1; j <= n; ++j) {
                    if(S & (1 << j - 1)) continue;
                    f[S | (1 << j - 1)][j] = min(f[S | (1 << j - 1)][j], f[S][i] + dis[i][j]);
                }
            }
        }
        memset(g, 0x3f, sizeof g);
        g[(1 << n - 1)][n] = 0;
        for(int S = 0; S < (1 << n); ++S) {
            int cnt = 0;
            for(int i = 1; i <= n; ++i) if(S & (1 << i - 1)) cnt++;
            if(cnt > N + 2) continue;
            for(int i = 1; i <= n; ++i) {
                if(!(S & (1 << i - 1))) continue;
                for(int j = 1; j <= n; ++j) {
                    if(S & (1 << j - 1)) continue;
                    g[S | (1 << j - 1)][j] = min(g[S | (1 << j - 1)][j], g[S][i] + dis[i][j]);
                }
    //            cout<<S<<" "<<i<<" "<<g[S][i]<<"
    ";
            }
        }
        dfs(0, 2, 0);
        cout<<ans<<"
    ";
        return 0;
    }
    
  • 相关阅读:
    201521044091《Java程序设计》第7周学习总结
    201521044091《java程序设计》第四次总结
    201521044091 《java程序设计》第八周学习总结
    201521044091 《Java程序设计》第5周学习总结
    201521044091 《Java程序设计》第2周学习总结
    201521044091 《Java程序设计》第3周学习总结
    MySQL设置字符集CHARACTER SET
    Create My MySQL configuration by Percona
    How to use jQuery to manipulate Cookies
    How to use OpenXml to import xml data to Sql server
  • 原文地址:https://www.cnblogs.com/Silymtics/p/test-GRYZ20211103.html
Copyright © 2020-2023  润新知