国家集训队题选做[2]

上一篇博客,因为某排版问题使用不良,所以转接到这里。

CF575A Fibonotci

傻逼题。
考虑维护一个周期的连乘矩阵结果,然后遇到特殊点就修改这个周期的这个矩阵。
线段树维护即可。

CF578E Walking!

思考到了一些东西。
我们考虑划分子序列,让其子序列中元素递增,且满足条件。
那么子序列就按端点状态有四种情况:\((1,1)(0,1)(1,0)(1,1)\)
考虑到除了\((0,1)(1,0)\)两种情况我们无法直接合并。
那我们来处理这种情况,我们把结尾序列位置小的放置在结尾序列位置大的后面,再拼接起来即可,这样我们就把两个以\(1\)的代价合并为了\((0,0) 或 (1,1)\)

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#define ll long long 
#define MAXN 100005

int n,tot;
int a[MAXN],id[MAXN];
std::vector<int> pos[MAXN],vec[2][2],tmp[2];
char s[MAXN];

void print(const std::vector<int> &v){
    for(int x : v){
        for(int y : pos[x])
            printf("%d ",y);
    }
}

int main(){
    scanf("%s",s + 1);
    n = strlen(s + 1);
    for(int i = 1;i <= n;i++){
        a[i] = (s[i] == 'R');
        if(tmp[a[i] ^ 1].empty())
            id[i] = ++tot;
        else
            id[i] = tmp[a[i] ^ 1].back(), tmp[a[i] ^ 1].pop_back();
        tmp[a[i]].push_back(id[i]);
        pos[id[i]].push_back(i);
    }
    for(int i = 1;i <= tot;i++)
        vec[a[pos[i][0]]][a[pos[i].back()]].push_back(i);
    if(!vec[1][0].empty() && !vec[0][1].empty() && vec[0][0].empty() && vec[1][1].empty()){
        int x = vec[1][0].back(), posx = pos[x].back();
        int y = vec[0][1].back(), posy = pos[y].back();
        int t = 0;
        if(posx > posy)
            std::swap(x,y), std::swap(posx,posy), t ^= 1;
        pos[y].pop_back();
        pos[x].push_back(posy);
        vec[t][t ^ 1].pop_back();
        vec[t ^ 1][t].pop_back();
        vec[t ^ 1][t ^ 1].push_back(x);
        vec[t][t].push_back(y);
    }
    int t = 0;
    if(vec[1][1].empty()) t ^= 1;
    printf("%d\n",tot - 1);
    print(vec[t][t]);
    print(vec[t ^ 1][t]);
    print(vec[t ^ 1][t ^ 1]);
    print(vec[t][t ^ 1]);
    return 0;
}

CF605E Intergalaxy Trips!

一道很经典的期望题。
考虑我们一个点有两种转移:
留在原地和从该点转移出去。
那么有\(E_x = p_x(E_x + z) + F_x\)
\(F_x\)为从该点转移出去的期望和。
那么就有\(E_x = \frac{z * p_x + F_x}{1-p_x}\)
那么回到本题。

本题有:

我们从\(n\)倒推回来,我们发现\(F_x\)只会被\(E_y < E_x\)更新,否则我们不如直接自环。

所以我们考虑一个出点\(j\)会被更新,当且仅当比他小的\(E_k\)这个点,没有出边。
\(p_i = \sum(E_j < E_i)(1-p_{i,j})\)
\(F_x = \sum[E_j < E_i](E_j +1)\prod[E_k < E_j](1-p_{i,k})\)
\(E_x = \frac{1 * p_x + F_x}{1-p_x}\)

所以用类似于\(dij\)的操作就行了。

#include<iostream>
#include<cstdio>
#define ll long long
#define N 1005

ll n,vis[N];
double F[N],p[N],a[N][N],E[N];

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i){
		for(int j = 1;j <= n;++j){
			ll x;
			scanf("%lld",&x);
			a[i][j] = 1.0 * x / 100;
		}
	}
	for(int i = 1;i <= n;++i)
	p[i] = 1;
	p[n] = 0;
	for(int i = 1;i <= n;++i){
		int u = 0;
		double minn = 3e18;
		for(int j = 1;j <= n;++j)
		if(!vis[j] && (p[j] + F[j]) / (1 - p[j]) < minn)
		minn = (p[j] + F[j]) / (1 - p[j]),u = j;
		vis[u] = 1;
		E[u] = (F[u] + p[u]) / (1 - p[u]);
		if(u == 1){
			printf("%.10f",E[u]);
			return 0;
		}
		for(int j = 1;j <= n;++j)
		if(!vis[j])F[j] += p[j] * a[j][u] * (E[u] + 1),p[j] *= 1 - a[j][u];
	}
}

CF626G Raffles

考虑我们可以对每一种彩票池进行差分。
每次选择差分最高的地方投票。
然后每次修改。
我们就直接从最小差分的票处,拿到最大差分处即可。
可以证明每次修改最多只有一张票改变。
拿muliset维护一下就行了。

posted @ 2021-09-02 20:28  fhq_treap  阅读(112)  评论(0编辑  收藏  举报