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\n", 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} \binom{d}{k} \sum_{j = 0}^{c - 1} j \times 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\n", 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<<"\n";
            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<<"\n";
        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<<"\n";
        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<<"\n";
        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]<<"\n";
        }
    }
    dfs(0, 2, 0);
    cout<<ans<<"\n";
    return 0;
}
posted @ 2021-11-03 11:42  Suzt_ilymtics  阅读(71)  评论(0编辑  收藏  举报