CF835
CF835A
CF835A题意
两个人参加打字比赛,已知一共有 s s s 个字符,两人的网络延迟分别是 t 1 , t 2 t_1,t_2 t1,t2,打每个字符的速度分别是 v 1 , v 2 v_1,v_2 v1,v2,一场比赛的用时是下载用时+打字用时+上传用时,请问谁的总用时最小
CF835A题解
总用时 T i = 2 × t i + s × v i T_i=2\times t_i+s\times v_i Ti=2×ti+s×vi,比一下就好了
CF835A代码
#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;
}
int s, v1, v2, t1,t2;
signed main(){
s = read(); v1 = read(); v2 = read(); t1 = read(); t2 = read();
if(2 * t1 + s * v1 < 2 * t2 + s * v2){puts("First");}
if(2 * t1 + s * v1 == 2 * t2 + s * v2){puts("Friendship");}
if(2 * t1 + s * v1 > 2 * t2 + s * v2){puts("Second");}
return 0;
}
CF835B
CF835B题意
给两个数 k , n k,n k,n,要改变 n n n 的尽量少的位数,使得 n n n 的每一位值之和不小于 k k k
CF835B题解
贪心的想,每一次把 n n n 中最小的位置换成 9 9 9,这样改变的位数最少
CF835B代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int k, sum;char ch[maxn];
priority_queue<int,vector<int>, greater<int> > que;
signed main(){
scanf("%d",&k); scanf("%s",ch + 1);int len = strlen(ch + 1);
for(int i = 1;i <= len;i++){que.push(ch[i] - '0');sum += int(ch[i] - '0');}
int cnt = 0;
// printf("%d\n",que.top());
while(sum < k && !que.empty()){sum += (9 - que.top());que.pop();cnt++;}
printf("%d\n",cnt);
return 0;
}
CF835C
CF835C题意
在一个笛卡尔系中有
n
(
n
≤
1
0
5
)
n(n \le 10^5)
n(n≤105) 个星星,最大亮度是
C
(
C
≤
10
)
C(C \le 10)
C(C≤10),第
i
i
i 个星星的坐标是
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi), 亮度是
s
i
s_i
si
但是这些星星的亮度会随时间变化,具体而言,设
t
t
t 时刻第
i
i
i 个星星的亮度为
f
i
(
t
)
f_i(t)
fi(t),那么
f
i
(
t
)
=
(
s
i
+
t
)
m
o
d
C
f_i(t)=(s_i+t) \space mod \space C
fi(t)=(si+t) mod C
现在给出
q
q
q 个询问,第
i
i
i 次询问,以
(
x
i
1
,
y
i
1
)
(x_{i1},y_{i1})
(xi1,yi1) 为左上角,
(
x
i
2
,
y
i
2
)
(x_{i2},y_{i2})
(xi2,yi2) 为右下角的矩阵中,在
t
i
t_i
ti 时刻的亮度之和
CF835C题解
如果设
(
i
,
j
)
(i,j)
(i,j) 位置的星星有
k
k
k 个(为
c
1
,
2
,
.
.
.
,
k
c_{1,2,...,k}
c1,2,...,k),那么在时间
T
T
T 的时候答案就是
a
n
s
i
,
j
,
T
=
∑
i
=
1
k
(
(
s
i
+
t
)
m
o
d
C
)
ans_{i,j,T}=\sum_{i=1}^k((s_i+t) \space mod \space C)
ansi,j,T=∑i=1k((si+t) mod C)
不难发现最外面没有
m
o
d
C
mod\space C
mod C,不能简单
m
o
d
mod
mod
发现
C
C
C 很小,而且时间每经过一个
C
C
C 周期答案就会循环一次,于是想到直接预处理
m
o
d
C
=
k
mod\space C=k
mod C=k 时的答案
然后就没有然后了
CF835C代码
#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 = 110, maxm = 1e5 + 10;
int cf[maxn][maxn][11];
int x[maxm], y[maxm], col[maxm];
int n, q, C;
signed main(){
n = read(); q = read(); C = read() + 1;
for(int i = 1;i <= n;i++){
x[i] = read(); y[i] = read(); col[i] = read();
for(int j = 0;j < C;j++){cf[x[i]][y[i]][j] += ((col[i] + j) % C);}
}
for(int k = 0;k < C;k++)
for(int i = 1;i <= 100;i++)
for(int j = 1;j <= 100;j++)
cf[i][j][k] = (cf[i][j][k] + cf[i - 1][j][k] + cf[i][j - 1][k] - cf[i - 1][j - 1][k]);
int t, xl, xr, yl, yr;
for(int i = 1;i <= q;i++){
t = read() % C; xl = read() - 1; yl = read() - 1; xr = read(); yr = read();
printf("%d\n",cf[xr][yr][t] - cf[xr][yl][t] - cf[xl][yr][t] + cf[xl][yl][t]);
}
return 0;
}
CF835D
CF835D题意
给出一个字符串
S
S
S(
∣
S
∣
≤
5000
|S|\le 5000
∣S∣≤5000),同时定义一个子串的等级
f
(
l
,
r
)
=
{
0
(
(
l
,
r
)
不是回文串
)
f
(
l
,
m
i
d
)
+
1
(
(
l
,
r
)
是回文串,
(
l
,
m
i
d
)
是
(
l
,
r
)
的左半子串
)
f(l,r)={\begin{cases} 0\,\,((l,r)不是回文串)\\ f(l,mid)+1\,\,((l,r)是回文串,(l,mid)是(l,r)的左半子串)\\ \end{cases}}
f(l,r)={0((l,r)不是回文串)f(l,mid)+1((l,r)是回文串,(l,mid)是(l,r)的左半子串)
CF835D题解
一个很显然的思路,类似区间DP的思想,暴力枚举子串(顺序为长度从小到大),判断这个子串的左半子串是不是回文串,然后加一下就好了
判断的话可以直接Hash
CF835D代码
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned 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 = 5e3 + 10;
const ull K = 27;
ull hashh[maxn][2], up[maxn];
char ch[maxn];
int n;
int pos[maxn][maxn];
ll ans[maxn];
ull Hash(int l,int r,int opt){
if(opt)return (hashh[l][opt] - hashh[r + 1][opt] * up[r - l + 1]);
return (hashh[r][opt] - hashh[l - 1][opt] * up[r - l + 1]);
}
signed main(){
scanf("%s",ch + 1);n = strlen(ch + 1);
up[0] = 1;
for(int i = 1;i <= n;i++){
hashh[i][0] = (hashh[i - 1][0] * K + (ull)(ch[i] - 'a'));
up[i] = up[i - 1] * K;
}
for(int i = n;i;i--){hashh[i][1] = (hashh[i + 1][1] * K + (ull)(ch[i] - 'a'));}
for(int i = 1;i <= n;i++)pos[i][i] = 1;
for(int len = 2;len <= n;len++){
for(int i = 1;i + len - 1 <= n;i++){
int j = i + len - 1,k,l;
if(len & 1){k = i + len / 2 - 1;l = i + (len + 1) / 2;}
else {k = i + len / 2 - 1;l = i + len / 2;}
if(Hash(i,k,0) == Hash(l,j,1) && Hash(i,k,1) == Hash(l,j,0)){
pos[i][j] = max(pos[i][j],pos[i][k] + 1);
}
}
}
for(int len = 1;len <= n;len++){
for(int i = 1;i + len - 1 <= n;i++){
int j = i + len - 1, k = i + (len + 1) / 2 - 1,l = i + len - len / 2;
ans[pos[i][j]]++;
// printf("pos[%d][%d]=%d\n",i,j,pos[i][j]);
}
}
// printf("hash(1,2)=%llu,hash(3,4)=%llu\n",Hash(1,2,0),Hash(3,4,1));
for(int i = n;i;i--)ans[i] += ans[i + 1];
for(int i = 1;i <= n;i++)printf("%lld ",ans[i]);
return 0;
}
CF835E
CF835E题意
这是一道交互题
有一个长度为
n
n
n 的数组
a
1
,
2
,
.
.
.
,
n
a_{1,2,...,n}
a1,2,...,n, 其中只有两个数字是
y
y
y,剩下的都是
x
x
x
每次你都可以给出一个下标集合
S
S
S,交互库会回答
a
S
1
x
o
r
a
S
2
x
o
r
.
.
.
x
o
r
a
S
k
a_{S_1} \space xor \space a_{S_2} \space xor \space ... \space xor \space a_{S_k}
aS1 xor aS2 xor ... xor aSk 的答案并返回给你
你最多问交互库
19
19
19 次问题
你需要回答出数字是
y
y
y 的数字在数组中的下标
CF835E题解
首先可以画出以下表格
S . s i z e ( ) S.size() S.size() | 含有 y y y 的数量 | 交互库的答案 |
---|---|---|
奇数 | 奇数 | y y y |
奇数 | 偶数 | x x x |
偶数 | 奇数 | x x o r y x\space xor\space y x xor y |
偶数 | 偶数 | 0 0 0 |
然后按照二进制分集合,询问交互库答案
可以得到答案编号的
x
o
r
xor
xor值(设为
t
m
p
tmp
tmp,第
i
i
i 位如果含有
y
y
y 的数量是奇数则为
1
1
1,否则为
0
0
0)
同时可以得到一个只含有一个
y
y
y 的集合
S
1
S_1
S1
通过二分集合
S
1
S_1
S1 可以得出一个答案编号(设为
a
n
s
1
ans_1
ans1)
因为
a
n
s
1
x
o
r
a
n
s
2
=
t
m
p
ans_1 \space xor \space ans_2 = tmp
ans1 xor ans2=tmp
所以
a
n
s
2
=
t
m
p
x
o
r
a
n
s
1
ans_2 = tmp \space xor \space ans_1
ans2=tmp xor ans1
最后别忘了把答案排下序
然后就没有然后了
CF835E代码
#include<bits/stdc++.h>
using namespace std;
int n, x, y, tmp;
vector<int> vec1,vec;
void solve(int l,int r){
if(l == r){
cout << "! " << min(vec1[l],(tmp ^ vec1[l])) << ' ' << max((tmp ^ vec1[l]),vec1[l])<< endl;
exit(0);
}
int mid = l + r >> 1;vec.clear();
for(int i = l;i <= mid;i++){vec.push_back(vec1[i]);}
cout << "? " << vec.size();for(int i : vec)cout << ' ' << i;cout << endl;
int res;cin >> res;
if(vec.size() % 2){
if(res == y){solve(l,mid);}
else solve(mid + 1,r);
}
else{
if(res == (x ^ y)){solve(l,mid);}
else solve(mid + 1,r);
}
}
signed main(){
cin >> n >> x >> y;
for(int i = 9;i + 1;i--){
vec.clear();
for(int j = 1;j <= n;j++){if(j & (1 << i))vec.push_back(j);}
if(!vec.size())continue;
cout << "? " << vec.size();for(int j : vec){cout << ' ' << j;}cout << endl;
int res;cin >> res;
if(vec.size() % 2){
if(res == y){
if(!vec1.size()){for(int j : vec)vec1.push_back(j);}
tmp |= (1 << i);
}
}
else{
if(res == (x ^ y)){
if(!vec1.size()){for(int j : vec)vec1.push_back(j);}
tmp |= (1 << i);
}
}
}
solve(0,vec1.size() - 1);
return 0;
}
CF835F
CF835F题意
给出一个有 n ( n ≤ 2 × 1 0 5 ) n(n \le 2 \times 10^5) n(n≤2×105) 个节点的基环树(有 n n n 条边),每条边有一个权值 w i w_i wi,现在需要删除一条边使得
- 所有节点仍联通
- 删去这条边后所得的树的直径尽可能小
现在需要求出这个最小值
CF835F题解
这个shaber题调了我半个月
细节超多,我们从头说起
首先找到这个环,如果断开这个环上所有边的话,各个在环上的顶点就变成了一个一个的什么啊的子树的根。
容易发现这些子树的边是不能断开的,于是先用树形DP求一下各个子树的直径(记其最大值为
a
n
s
1
ans1
ans1),并求出每个顶点到自己子树中最长链的值
v
a
l
i
val_i
vali。
然后尝试求断开这个拥有
k
k
k 个节点的环上一条边之后的直径最小值(记为
a
n
s
2
ans2
ans2)。
首先先按顺时针(逆时针也行)将环铺开,记录第
i
i
i 个节点的权值
v
a
l
i
val_i
vali 和通向下一个节点的边权
w
e
i
i
wei_i
weii (特别的,
w
e
i
k
wei_k
weik 为
k
k
k 通向
1
1
1 的边权)。
然后考虑如果断开
i
,
i
+
1
i,i+1
i,i+1 这条边之后,直径的取值可能是什么。
直径可能是
[
1
,
i
]
[1,i]
[1,i] 或
[
i
+
1
,
k
]
[i+1,k]
[i+1,k] 中的某一条路径成为直径,或者是通过
n
,
1
n,1
n,1(也就是将一段
[
x
,
k
]
[x,k]
[x,k] 和另一段
[
1
,
y
]
[1,y]
[1,y] 拼在一起)的某一条路径成为路径。
这三种都是可以维护的,然后枚举一下删哪条边就好了(删除
[
k
,
1
]
[k,1]
[k,1] 这条边需要特判一下)
CF835F注意事项
- 树的直径如果用DP求的话,直径是所有点 i i i 的 max ( d p i , 0 + d p i , 1 ) \max(dp_{i,0}+dp_{i,1}) max(dpi,0+dpi,1) 而不是只有根节点可以统计
- 特判 [ n , 1 ] [n,1] [n,1] 这条边的时候需要将 a n s 2 ← 0 ans2 \gets 0 ans2←0 然后再统计,别赋值成 I N F INF INF 然后这种就统计不到了
- 这道题确实是一道基环树的好题,还有双倍经验(将这道题的答案除以 2.0 2.0 2.0 就是双倍经验的答案了)
CF835F代码
#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 = 3e5 + 10;
int n;
int head[maxn], tot = 1;
struct edge{
int from, to, nexte, wei;
edge(int fr = 0,int to = 0,int ne = 0,int we = 0):from(fr),to(to),nexte(ne),wei(we){}
}e[maxn << 1];
void add(int u,int v,int w){e[++tot] = edge(u,v,head[u],w);head[u] = tot;}
bool book[maxn];
int fat[maxn];
bool ring[maxn];
bool dfs1(int u,int father){
// printf("u = %d\n",u);
book[u] = 1;
for(int i = head[u];i;i = e[i].nexte){
int v = e[i].to;if(v == fat[u])continue;
fat[v] = u;
if(!book[v]){if(dfs1(v,u))return 1;}
else{
int tmp = u;
while(1){
// printf("top() = %d\n",tmp);
ring[tmp] = 1;tmp = fat[tmp];
if(tmp == u)break;
}
return 1;
}
}
return 0;
}
int f[maxn][2];
void dfs2(int u,int father){
for(int i = head[u];i;i = e[i].nexte){
int v = e[i].to, w = e[i].wei;
if(v == father || ring[v])continue;
dfs2(v, u);
if(f[v][0] + w > f[u][0]){f[u][1] = f[u][0];f[u][0] = f[v][0] + w;}
else if(f[v][0] + w > f[u][1]){f[u][1] = f[v][0] + w;}
}
}
int wei[maxn], val[maxn];
int idx, mp[maxn];
void dfs3(int u,int father){
if(book[u]){return;}book[u] = 1;
idx++; val[idx] = f[u][0];mp[u] = idx;
for(int i = head[u];i;i = e[i].nexte){
int v = e[i].to, w = e[i].wei;
if(!ring[v] || v == father)continue;
wei[idx] = w;dfs3(v, u);break;
}
}
int suf[maxn], pre[maxn];
int pre1[maxn], suf1[maxn];
signed main(){
// #ifndef ONLINE_JUDGE
// freopen("P1399_4.in","r",stdin);
// #endif
n = read();int u, v, w;
for(int i = 1;i <= n;i++){
u = read(); v = read(); w = read();
add(u, v, w);add(v, u, w);
}
/*找基环*/ dfs1(1, 1);
// puts("11111");
/*算各自子树最大值,并统计各自子树直径*/
int ans2 = 0;
for(int i = 1;i <= n;i++)
if(ring[i]){dfs2(i,i);}
for(int i = 1;i <= n;++i)ans2 = max(ans2,f[i][0] + f[i][1]);
// puts("22222");
// for(int i = 1;i <= n;i++)printf("ring[%d]=%d\n",i,ring[i]);
/*将环转变成链并重新编号*/
memset(book,0,sizeof(book));
for(int i = 1;i <= n;i++){if(ring[i]){dfs3(i,i);break;}}
// for(int i = 1;i <= n;i++){printf("mp[%d] = %d\n",i,mp[i]);}
/*统计答案*/
pre[1] = val[1];int tmp = 0;
for(int i = 2;i <= idx;i++){
tmp += wei[i - 1];
pre[i] = max(pre[i - 1],val[i] + tmp);
}
suf[idx] = val[idx];tmp = 0;
for(int i = idx - 1;i;i--){
tmp += wei[i];
suf[i] = max(suf[i + 1], val[i] + tmp);
}
// for(int i = 1;i <= idx;i++)printf("pre[%d] = %d\n",i,pre[i]);
// for(int i = 1;i <= idx;i++)printf("suf[%d] = %d\n",i,suf[i]);
int ans1 = 0, prex = val[1]; pre1[1] = val[1];
for(int i = 1;i < idx;i++){
pre1[i + 1] = prex + wei[i] + val[i + 1];
prex += wei[i];prex = max(prex,val[i + 1]);
}
for(int i = 2;i <= idx;i++){pre1[i] = max(pre1[i],pre1[i - 1]);}
int sufx = val[idx]; suf1[idx] = val[idx];
for(int i = idx;i > 1;i--){
suf1[i - 1] = sufx + wei[i - 1] + val[i - 1];
sufx += wei[i - 1];sufx = max(sufx,val[i - 1]);
}
for(int i = idx - 1;i;i--)suf1[i] = max(suf1[i],suf1[i + 1]);
for(int i = 1;i <= idx;i++){ans1 = max(ans1,pre1[i]);}
// printf("%lld~%lld : %lld\n",idx,1,ans1);
for(int i = 1;i < idx;i++){
/*现在是,断开i~i+1时间*/
ans1 = min(ans1,max(max(pre1[i],suf1[i + 1]),pre[i] + suf[i + 1] + wei[idx]));
// printf("%lld~%lld : %lld\n",i,i + 1,ans1);
}
printf("%lld\n",max(ans1,ans2));
// printf("ans1=%lld ans2=%lld\n",ans1,ans2);
// printf("idx = %lld\n",idx);
// for(int i = 1;i <= idx;i++){printf("val[%lld] = %lld, wei[%lld] = %lld\n",i,val[i],i,wei[i]);}
// for(int i = 1;i <= idx;i++){printf("pre[%lld] = %lld, pre1[%lld] = %lld\n",i,pre[i],i,pre1[i]);}
// for(int i = 1;i <= idx;i++){printf("suf[%lld] = %lld, suf1[%lld] = %lld\n",i,suf[i],i,suf1[i]);}
return 0;
}