国家集训队题选做[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维护一下就行了。