CF238题解
CF238
CF238A
CF238A题意
给出两个整数 \(n,m\),现在问你有多少个序列 \(a\) 满足:
- 序列长度为 \(n\)。
- \(a_i\in[0,2^m-1]\)
- \(\forall 1\le i\le j \le n,a_i\oplus a_{i+1}\oplus\dots\oplus a_j\neq0\)
答案对 \(10^9+9\) 取模。
CF238A题解
不难想到转化题意,即设 \(s_i=\oplus_{j=1}^ia_j\),那么题意转化为 \(\forall 0\le i< j \le n,s_i\oplus s_j\neq0\)。
然后发现这个相当于在 \([1,2^m-1]\) 中选不同的 \(n\) 个数。
然后就没了。
CF238A代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10, mod = 1e9 + 9;
int n, m;
signed main(){
n = read(); m = read();int ans = 1, pw = 1;
for(int i = 1;i <= m;i++){pw = (pw * 2ll) % mod;}pw = (pw - 1 + mod) % mod;
for(int i = 0;i < n;i++){ans = ans * (pw - i + mod) % mod;}
printf("%lld\n",ans);
return 0;
}
CF238B
CF238B题意
给出一个长度为 \(n\) 的数组 \(a(a_i\ge 0)\) 和一个数字 \(h\)。
现在需要你将其划分成两个序列(可以有一个是空的),使得优美度最小。
定义一个函数 \(f(i,j)\) 表示,当 \(i,j\) 被划分为一个集合时,\(f(i,j)=a_i+a_j\),否则 \(f(i,j)=a_i+a_j+h\)。
一个划分方式的优美度定义为 \(\max{f(i,j)}-\min{f(i,j)}\)。
需要你给出方案。
CF238B题解
我们先将数组都归到第一个序列中,然后看哪些需要挪到第二个序列。
经过简单的思考,发现只有最小的那个数字所在的序列发生改变时,答案有可能改变。
然后比一下就OK了。
CF238B代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10;
int n, h;
struct node{
int val, id;
node(int val = 0,int id = 0):val(val),id(id){}
friend bool operator < (node a,node b){return a.val < b.val;}
}a[maxn];
bool cmp(node a,node b){return a.id < b.id;}
signed main(){
n = read();h = read();
for(int i = 1;i <= n;i++)a[i] = node(read(),i);
sort(a + 1,a + 1 + n);
int val1 = max(h + a[n].val + a[1].val,a[n].val + a[n - 1].val) - min(a[1].val + a[2].val + h,a[2].val + a[3].val);
int val2 = a[n].val + a[n - 1].val - a[1].val - a[2].val;
if(val1 < val2){a[1].val = -1;}
sort(a + 1,a + 1 + n,cmp);
printf("%d\n",min(val1,val2));
for(int i = 1;i <= n;i++){
putchar((a[i].val == -1) ? '1' : '2');
putchar(' ');
}
puts("");
return 0;
}
CF238C
CF238C题意
给你一棵有 \(n\) 个节点的树,每条边有一个方向。问你最少经过多少次变换,使得整棵树没有入度的点不超过两个。
\(n\le 3000\)
CF238C题解
这里有一个 \(O(nk^2)(k=2)\) 的做法。
设 \(f_{u,i,j}\) 表示以 \(u\) 为根的子树中,有 \(i\) 个点(包括自己)没有入度,自己 (\(j=0/1\iff\) 有/没有) 入度的最少次数。
显然,对于 \(u\) 的儿子 \(v\),如果设 \(w\) 表示改变方向到 \(u\to v\) 的花费,有
没了。
CF238C代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 3e3 + 10;
int n;
int f[maxn][3][2];
vector<pair<int,int> > edg[maxn];
void dfs(int u,int fa){
f[u][1][1] = 0;
for(auto i : edg[u]){
int v = i.first, w = i.second;
if(v != fa){
int g[3][2];memset(g,0x3f,sizeof(g));
dfs(v, u);
for(int i = 0;i < 3;i++)
for(int j = 0;j < 3 - i;j++)
for(int kx = 0;kx < 2;kx++)
for(int ky = 0;ky < 2;ky++){
g[i + j][kx] = min(g[i + j][kx], w + f[u][i][kx] + f[v][j + ky][ky]);
g[i + j][0 ] = min(g[i + j][0 ],!w + f[u][i + kx][kx] + f[v][j][ky]);
}
memcpy(f[u],g,sizeof(g));
}
}
}
signed main(){
n = read();int u, v;
for(int i = 1;i < n;i++){
u = read(); v = read();
edg[u].push_back(make_pair(v, 0));
edg[v].push_back(make_pair(u, 1));
}
memset(f,0x3f,sizeof(f));
dfs(1, 0);int ans = 0x3f3f3f3f;
for(int i = 0;i <= 2;i++)
for(int j = 0;j < 2;j++)
ans = min(ans,f[1][i][j]);
printf("%d\n",ans);
return 0;
}
CF238D
CF238D题意
有一种编程语言,在这种语言下一个程序可以用由数字、\(<\) 和 \(>\) 组成的字串表示。程序运行时有一个指针,移动方向向右。
我们重复以下操作直到指针移动到字串外。
- 如果指针指向一个数字,则输出这个数字并将数字减 \(1\)。如果输出的数字为 \(0\),则将它从字串中删除。
- 如果指针指向 \(<\) 或 \(>\),则改变移动方向,并以这个方向移动一格。若为 \(<\) 则移动方向向左,若为 \(>\) 则移动方向向右。若移动后指针仍然指向 \(<\) 或 \(>\),则将移动前指向的字符删除。
对于给出的长度为 \(n\) 的字串,有 \(q\) 组询问,每次询问子串 \([l,r]\) 作为一段程序时,每个数字的输出次数为多少。
CF238D题解
完全想不到思路(
发现可以 \(O(nd)(d=10)\) 的计算一个区间的贡献,但是显然会炸没的。
发现炸是因为重复计算太多东西了,考虑一次计算就算出所有答案。
在最左边放>防止越界。
然后定义 \(f_{0/1,i}\) 分别表示从 \(i\) 出发第一次准备向左移动时的答案和第一次准备到达 \(i\) 时的答案的前缀和。
因为一段区间结束是从最左端或者最右端出来,而从这个定义显然是时间更靠前的那个(时间更靠前的那个一定前缀和更小)是出来的前缀和。进来的前缀和显然。
那么答案就变成了 \(\min{(f_{1,r+1}-f_{0,l})}-f_{1,l}\)。
值得注意的一点是,有的时候 \(x\) 已经被删除了,但是没有更新出来的答案。这个时候并查集做一下就好。
CF238D代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10;
int n, q;
char ch[maxn];
int len, l[maxn], r[maxn];
bool book[2][maxn];
int f[2][maxn][10];
int fa[maxn];
int getf(int x){return fa[x] == x ? x : fa[x] = getf(fa[x]);}
void merg(int x,int y){int fx = getf(x), fy = getf(y);if(fx != fy)fa[fy] = fx;}
int pos, opt, buc[10];
void del(int x){r[l[x]] = r[x];l[r[x]] = l[x];len--;}
void move(){
int nxt = 0;
if(isdigit(ch[pos])){
buc[ch[pos] - '0']++;
if(ch[pos] == '0')del(pos);
else ch[pos]--;
nxt = opt ? r[pos] : l[pos];
}
else{
if(ch[pos] == '<')opt = 0;
else opt = 1;
nxt = opt ? r[pos] : l[pos];
if(pos && !isdigit(ch[nxt]))del(pos);
}
if(opt == 1 && !book[1][nxt]){
for(int i = 0;i < 10;i++)f[1][nxt][i] = buc[i];
book[1][nxt] = 1;
}
if(opt == 0){
int j = getf(nxt) + 1;
while(j <= pos){
book[0][j] = 1;
for(int i = 0;i < 10;i++)f[0][j][i] = buc[i];
merg(j,j - 1);j = getf(j) + 1;
}
}
pos = nxt; return;
}
signed main(){
n = read(); q = read();
scanf("%s",ch + 1);ch[0] = '>';
for(int i = 1;i <= n;i++){fa[i] = i;r[i] = i + 1;l[i] = i - 1;}
memset(f,0x3f,sizeof(f));
r[0] = 1;opt = 1;pos = 0;len = n;
while(len > 0 && pos <= n){move();}
for(int j = 1;j <= q;j++){
int l = read(), r = read();
for(int i = 0;i < 10;i++)
printf("%d ",(book[0][l] ? min(f[0][l][i],f[1][r + 1][i]) : f[1][r + 1][i]) - f[1][l][i]);
puts("");
}
return 0;
}
CF238E
CF238E题意
给出一张 \(n\) 个点 \(m\) 条边的有向图。每条边的长度相同(都是一)。Urpal想从 \(a\) 坐车到 \(b\)。有 \(k\) 条路线,每条路线都会在每一秒从 \(s_i\) 发出一辆公交车,随机选择一条最短路到 \(t_i\)。如果不能从 \(s_i\to t_i\),那么这条路线就不会发车。只要一辆车经过Urpal,他就可以上车。他可以在这辆车行进到任意位置时下车。
现在Urpal想知道,他能否在有限的时间内到达目的地,以及在最坏的情况下,最少换乘次数。
任意时刻,Urpal只知道自己的位置和想去的位置。当他上车之后他只知道这辆车是哪辆车。当然,他知道整张图和每个路线的起止地点。
但是Urpal并不知道这辆车的具体路线。
告诉他最少换乘次数,或者告诉他最坏情况下,他不可能在有限时间到达终点。
CF238E题解
首先发现两个性质:
- 如果 \(a\)(或 \(b\))不在一条路线 \(i\) 的必经之路上,那么这个路线就一定不会经过这个点。
- 如果 \(x\) 为你现在所在的点,\(i\) 是你乘坐的路线,那么这个路线一定给你送到最劣的位置。
然后发现你的状态可以设计成 \((x,i)\) 表示你在 \(x\) 位置,现在乘坐的是第 \(i\) 条线路。
然后设 \(f_{x,i}\) 表示状态是 \((x,i)\) 时到达终点的最小代价,\(x\to y\) 表示存在一条从 \(x\) 到 \(y\) 的边。
发现转移只有两种:
至于怎么判断可能经过和一定经过,你可以先跑一个全源最短路,然后对于 \(x\to y\) 的路径,如果需要判断 \(z\) 是否可能被经过,那么当 \(e_{x,z}+e_{z,y}=e_{x,y}\) 的时候,这个点就是可能经过的。
那一定经过呢?只需要判断从 \(x\) 出发的、长度为 \(e_{x,z}\) 的,且可能在最短路上的 \(z\) 的数量,如果只有一个那么就是一定经过。
然后从终点反向递推到起点即可。
CF238E代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 100 + 10;
int n, m, S, T;
int e[maxn][maxn];
int K, st[maxn], ed[maxn];
bool avail[maxn];
int tot[maxn][maxn];
int deg[maxn][maxn];
int f[maxn][maxn];
signed main(){
n = read(); m = read(); S = read(); T = read();
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
e[i][j] = (i != j) * (0x3f3f3f3f);
for(int i = 1;i <= m;i++){int u = read(), v = read();e[u][v] = 1;}
for(int k = 1;k <= n;k++)
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
e[i][j] = min(e[i][j],e[i][k] + e[k][j]);
K = read();
for(int i = 1;i <= K;i++){st[i] = read();ed[i] = read();avail[i] = e[st[i]][ed[i]] != 0x3f3f3f3f;}
for(int i = 1;i <= n;i++)
for(int j = 1;j <= K;j++){
if(!avail[j] || e[st[j]][i] + e[i][ed[j]] != e[st[j]][ed[j]])continue;
tot[j][e[st[j]][i]]++;
for(int k = 1;k <= n;k++)
if(e[i][k] == 1 && e[st[j]][i] + e[i][k] + e[k][ed[j]] == e[st[j]][ed[j]])
deg[i][j]++;
}
memset(f,0x3f,sizeof(f));
for(int i = 1;i <= K;i++){
if(avail[i] && e[st[i]][T] + e[T][ed[i]] == e[st[i]][ed[i]])
f[T][i] = 0;
}
for(int k = 0;k < n;k++){
queue<pair<int,int> > que;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= K;j++)
if(f[i][j] == k){que.push(make_pair(i, j));}
while(!que.empty()){
int x = que.front().first, i = que.front().second;que.pop();
if(tot[i][e[st[i]][x]] == 1){
if(x == S){printf("%d\n",f[x][i] + 1);return 0;}
for(int j = 1;j <= K;j++)
if(avail[j] && e[st[j]][x] + e[x][ed[j]] == e[st[j]][ed[j]])
f[x][j] = min(f[x][j],f[x][i] + 1);
}
for(int y = 1;y <= n;y++){
if(e[y][x] == 1 && e[st[i]][y] + e[y][x] + e[x][ed[i]] == e[st[i]][ed[i]]){
if(!--deg[y][i] && f[y][i] > f[x][i]){
f[y][i] = f[x][i];
que.push(make_pair(y,i));
}
}
}
}
}
puts("-1");
return 0;
}

浙公网安备 33010602011771号