CF76
CF76A
CF76A题意
给出一个图( n ≤ 2 × 1 0 2 , m ≤ 5 × 1 0 4 n \le 2\times 10^2,m\le 5\times 10^4 n≤2×102,m≤5×104),每条边有两个属性 ( g i , s i ) (g_i,s_i) (gi,si),给出常数 G , S G,S G,S,求一个生成树 T T T 使得 G × max ( g i ) + S × max ( s i ) G \times \max(g_i) + S \times \max(s_i) G×max(gi)+S×max(si) 最小,求出这个值。
CF76A题解
一眼顶针,鉴定为乱搞题
首先先将所有边按照
g
i
g_i
gi 排序,然后从小到大枚举
s
i
s_i
si,每次只加入
s
j
>
s
i
s_j > s_i
sj>si 的边,然后跑
k
r
u
s
k
a
l
kruskal
kruskal。
但是这样显然是
O
(
m
2
)
O(m^2)
O(m2) 的,考虑优化。
注意到如果在一次
k
r
u
s
k
a
l
kruskal
kruskal 的时候,一条边没有用到,那么以后就永远用不到了,因为选这条边显然不会优于之前的某种选法。
而且
n
n
n 又这么小,每次只需要维护
n
−
1
n - 1
n−1 条边就好了。
然后就可以对每个
s
i
s_i
si 算出对应的答案啦。
于是就可以
O
(
n
m
)
O(nm)
O(nm) 快乐地过啦!
CF776A优化
- 注意到刚开始至少需要选 n − 1 n - 1 n−1条边,可以先二分一个答案,然后从这个位置开始枚举 s i s_i si。
- 维护边用的集合可以用 m u l t i s e t multiset multiset。
CF76A代码
#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 = 2e2 + 10, maxm = 5e4 + 10;
int n, m;
int G, S;
struct edge{
int u, v, g, s, nexte;
edge(int u = 0,int v = 0,int g = 0,int s = 0,int ne = 0):u(u),v(v),g(g),s(s),nexte(ne){}
friend bool operator < (edge a,edge b){return a.s < b.s;}
}e[maxm],edg;
int gold[maxm];
int fa[maxn];
int getf(int x){return fa[x] == x ? x : fa[x] = getf(fa[x]);}
multiset<edge> E;
typedef multiset<edge>::iterator iter;
bool check(int mid){
int cnt = 1;
for(int i = 1;i <= n;i++){fa[i] = i;}
for(iter it = E.begin();it != E.end();it++){
edg = *it;int fu = getf(edg.u), fv = getf(edg.v);
if(fu == fv || edg.g > mid)continue;
fa[fu] = fv;cnt++;
}
return cnt >= n;
}
signed main(){
int u, v, g, s, ans = 0x3f3f3f3f3f3f3f3f;
n = read(); m = read();G = read(); S = read();
for(int i = 1;i <= m;i++){
u = read(); v = read(); g = read(); s = read();
gold[i] = g;
e[i] = edge(u,v,g,s);E.insert(e[i]);
}
sort(gold + 1,gold + 1 + m);
int l = 1, r = m, res = -1;
while(l <= r){
int mid = l + r >> 1;
if(check(gold[mid])){r = mid - 1;res = mid;}
else l = mid + 1;
}
if(res == -1){puts("-1");return 0;}
for(int point = res;point <= m;point++){
int cnt = 1, maxx = 0;
for(int j = 1;j <= n;j++)fa[j] = j;
for(iter it = E.begin();it != E.end();it++){
edg = *it;int fu = getf(edg.u), fv = getf(edg.v);
if(edg.g > gold[point])continue;
if(fu == fv){iter tmp = it;tmp--;E.erase(it);it = tmp;}
else{fa[fu] = fv;maxx = edg.s;cnt++;}
if(cnt == n)break;
}
if(cnt == n)ans = min(ans,maxx * S + gold[point] * G);
}
printf("%lld\n",ans == 0x3f3f3f3f3f3f3f3f ? -1 : ans);
return 0;
}
CF76B
CF76B题意
在平面上给出两条直线
y
=
Y
0
y = Y_0
y=Y0 和
y
=
Y
1
y = Y_1
y=Y1。在
y
=
Y
0
y = Y_0
y=Y0 上有
n
n
n 只老鼠,第
i
i
i 只老鼠横坐标为
x
1
,
i
x_{1, i}
x1,i,在
y
=
Y
1
y = Y_1
y=Y1 上有
m
m
m 个奶酪,第
i
i
i 个奶酪横坐标为
x
2
,
i
x_{2, i}
x2,i。已知一只老鼠的捕食策略如下:
1.
1.
1. 如果离这只老鼠最近的奶酪有且只有一个,那么这只老鼠会往这个奶酪移动。
2.
2.
2. 如果有多个奶酪离老鼠距离最近,那么这只老鼠会选择向使所有老鼠中挨饿个数最少的奶酪移动。
每只老鼠的移动速度都是一样的,当某些老鼠到达某个奶酪并且当前奶酪还没有被吃掉时,他们会吃掉奶酪并且不再挨饿。如果某个老鼠在到达奶酪时奶酪已经被吃掉了,那么它不会再进行移动,此时我们认为它处于挨饿状态。
请输出最终处于挨饿状态的老鼠个数。
CF76B题解
考虑贪心的找答案,枚举对于每只老鼠,如果只满足第 1 1 1 中情况就没办法了,但是如果满足第 2 2 2 种情况,那么尽可能的让这只老鼠向左找\
CF76B代码
#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,INF = 0x3f3f3f3f3f3f3f3f;
int n, m;
int x[maxn], y[maxn];
int minn[maxn];
signed main(){
n = read(); m = read();read();read();
for(int i = 1;i <= n;i++)x[i] = read();
for(int i = 1;i <= m;i++)y[i] = read();
int ans = 0;
y[0] = -INF, y[m + 1] = INF;m++;sort(y,y + m + 1);
memset(minn,0x3f,sizeof(minn));
for(int i = 1;i <= n;i++){
int j = lower_bound(y,y + m + 1,x[i]) - y;j--;
// printf("j=%lld\n",j);
int dis = min(x[i] - y[j],y[j + 1] - x[i]);
if(y[j + 1] - x[i] > dis || (x[i] - y[j] == dis && (minn[j] == INF || minn[j] == dis))){j--;}
if(minn[j + 1] == INF || minn[j + 1] == dis)ans++;
minn[j + 1] = min(minn[j + 1],dis);
// printf("i=%lld : %lld\n",i,ans);
}
printf("%lld\n",n - ans);
return 0;
}
CF76C
CF76C题意
给出一个字符串
S
S
S (字符种类少于
22
22
22 种)和一个代价上限
t
t
t
同时给出一个数组
a
i
a_i
ai 表示删除第
i
i
i 种字符的花费,一个矩阵
c
o
s
t
i
,
j
cost_{i,j}
costi,j 表示删除字符后的字符串
T
T
T 中每对相邻的字母需要花费
c
o
s
t
T
i
,
T
i
+
1
cost_{T_i,T_{i+1}}
costTi,Ti+1
求出最终花费小于代价上限的方案
CF76C题解
首先思考下暴力怎么写。
每次暴力枚举删除的字符集合,然后再扫一遍相邻的花费。
时间复杂度
O
(
2
k
n
)
O(2^kn)
O(2kn) 显然过不了。\
发现复杂度瓶颈在于计算相邻花费,思考如何优化。
不难发现,如果想让一对点对
(
i
,
j
)
(i,j)
(i,j) 相邻,必须删除所有两者之间的字符,并且这两者之间不能有和这两个相同的字符
然后就非常的状压
设
f
S
f_S
fS 表示删除的字符集合是
S
S
S 时的花费
首先让
f
i
=
∑
j
∈
i
a
j
f_i = \sum_{j\in i}a_j
fi=∑j∈iaj
记录
s
t
a
j
sta_j
staj 表示到前面的字符
j
j
j 之间需要删除哪些字符(初始为
−
1
-1
−1)
然后枚举
S
S
S 中的每一位
i
i
i\
- 如果
s
t
a
j
≠
−
1
sta_j \neq -1
staj=−1
- 如果
S
i
∉
s
t
a
j
S_i\notin sta_j
Si∈/staj 且
j
∉
s
t
a
j
j\notin sta_j
j∈/staj
则 f s t a j + = c o s t j , S i f_{sta_j} += cost_{j,S_i} fstaj+=costj,Si
但是这样统计的话会让删除了 j j j 或 S i S_i Si 的情况也加了这种贡献,故
f s t a j ∣ ( 1 < < S i ) − = c o s t j , S i f_{sta_j | (1<<S_i)}-= cost_{j,S_i} fstaj∣(1<<Si)−=costj,Si
f s t a j ∣ ( 1 < < j ) − = c o s t j , S i f_{sta_j | (1<<j)}-= cost_{j,S_i} fstaj∣(1<<j)−=costj,Si
但是同时删除了 j , S i j,S_i j,Si 的情况减了两次这个贡献,故
f s t a j ∣ ( 1 < < j ) ∣ ( 1 < < S i ) + = c o s t j , S i f_{sta_j | (1<<j) | (1<<S_i)}+= cost_{j,S_i} fstaj∣(1<<j)∣(1<<Si)+=costj,Si - 让 s t a j ∣ = ( 1 < < S i ) sta_j |= (1<<S_i) staj∣=(1<<Si)\
- 如果
S
i
∉
s
t
a
j
S_i\notin sta_j
Si∈/staj 且
j
∉
s
t
a
j
j\notin sta_j
j∈/staj
这一位枚举完了之后记得把
s
t
a
S
i
=
0
sta_{S_i} = 0
staSi=0
那么最终统计答案的时候先让
f
i
=
f
i
+
∑
j
∈
i
f
i
x
o
r
j
f_i = f_i+\sum_{j\in i}f_{ixorj}
fi=fi+∑j∈ifixorj
然后枚举每一种删除方式集合
s
t
a
sta
sta,如果
f
s
t
a
≤
t
f_{sta}\le t
fsta≤t 且
s
t
a
sta
sta 是所有字符构成的集合的真子集,那么
a
n
s
+
+
ans++
ans++
CF76C细节
这道题虽说了只含有前
k
k
k 个字符,可没说这
k
k
k 个字符都有吃我四发提交
CF76C代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 2e5 + 10,maxk = 25;
int f[1 << maxk - 2], n, K, T;
char ch[maxn];
int a[maxn];
int cost[maxk][maxk];
int t[maxk], sta[maxk];
signed main(){
scanf("%lld%lld%lld",&n,&K,&T);
int all = 0;
scanf("%s",ch + 1);for(int i = 1;i <= n;i++){a[i] = ch[i] - 'A';all |= (1 << a[i]);}
for(int i = 1;i <= K;i++){scanf("%lld",&t[i]);f[1 << (i - 1)] += t[i];}
for(int i = 1;i <= K;i++)
for(int j = 1;j <= K;j++)
scanf("%lld",&cost[i - 1][j - 1]);
memset(sta,-1,sizeof(sta));
for(int i = 1;i <= n;i++){
for(int j = 0;j < K;j++)
if(sta[j] >= 0){
if(!((sta[j] >> a[i]) & 1) && !((sta[j] >> j) & 1)){
f[sta[j]] += cost[j][a[i]];
f[sta[j] | (1 << j)] -= cost[j][a[i]];
f[sta[j] | (1 << a[i])] -= cost[j][a[i]];
f[sta[j] | (1 << j) | (1 << a[i])] += cost[j][a[i]];
}
sta[j] |= (1 << a[i]);
}
sta[a[i]] = 0;
}
int ans = 0;
for(int i = 0;i < K;i++)
for(int j = 0;j < 1 << K;j++)
if((j >> i) & 1)f[j] += f[j ^ (1 << i)];
for(int i = 0;i < 1 << K;i++)
if(f[i] <= T && i != all && (i & all) == i){ans++;}
printf("%lld\n",ans);
return 0;
}
CF76D
CF76D题意
给你两个数 A , B A,B A,B,求一对满足 X ⊕ Y = B , X + Y = A X \oplus Y=B,X+Y=A X⊕Y=B,X+Y=A,且令 X X X 尽可能小,无解输出 − 1 -1 −1
CF76D题解
这啥啊
可知
A
−
B
A-B
A−B 其实就是
A
a
n
d
B
A \space and \space B
A and B 再向左移一位的结果,让
X
←
A
−
B
2
X \gets \frac{A-B}{2}
X←2A−B 显然最优
然后特判下
A
<
B
A<B
A<B 或者
2
∤
(
A
−
B
)
2\nmid(A-B)
2∤(A−B) 的时候无解就没了
CF76D代码
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
signed main(){
ull a, b;
cin >> a >> b;
if(a < b || (a - b) % 2 != 0)cout << -1 << endl;
else cout << (a - b) / 2 << " " << a - (a - b) / 2 << endl;
return 0;
}
CF76E
CF76E题意
给 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n≤105) 个点 ( x i , y i ) (x_i,y_i) (xi,yi),求两两点距离的平方和
CF76E题解
先看横坐标
手推一下柿子:
∑
i
=
1
n
∑
j
=
i
+
1
n
(
x
i
−
x
j
)
2
=
∑
i
=
1
n
∑
j
=
1
n
x
i
2
+
x
j
2
−
2
×
x
i
×
x
j
=
(
n
−
1
)
×
∑
i
=
1
n
x
i
2
−
∑
i
=
1
n
x
i
×
[
(
∑
j
=
1
n
x
j
)
−
x
i
]
\sum_{i=1}^n\sum_{j=i+1}^n(x_i-x_j)^2=\sum_{i=1}^n\sum_{j=1}^nx_i^2+x_j^2-2\times x_i \times x_j=(n-1)\times \sum_{i=1}^nx_i^2-\sum_{i=1}^nx_i\times [(\sum_{j=1}^nx_j) - x_i]
∑i=1n∑j=i+1n(xi−xj)2=∑i=1n∑j=1nxi2+xj2−2×xi×xj=(n−1)×∑i=1nxi2−∑i=1nxi×[(∑j=1nxj)−xi]
纵坐标同理
然后就没了
CF76E代码
#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;
int n;
int x[maxn], y[maxn];
int sumx, summx, sumy,summy;
signed main(){
n = read();
for(int i = 1;i <= n;i++){
x[i] = read();y[i] = read();
sumx += x[i];summx += (x[i] * x[i]);
sumy += y[i];summy += (y[i] * y[i]);
}
int ans = 0;
for(int i = 1;i <= n;i++){
ans -= x[i] * (sumx - x[i]);
ans -= y[i] * (sumy - y[i]);
}
ans += (n - 1) * (summx + summy);
printf("%lld\n",ans);
return 0;
}
CF76F
CF76F题意
在一个数轴上有 n n n 个事件,每个事件的坐标是 x i x_i xi,会在 t i t_i ti 这一时刻出现(之后消失),请问
- 如果从原点在 0 0 0 时刻出发,那么最多能看到多少个事件
- 如果可以从任意位置出发,那么最多能看到多少个事件
CF76F题解
首先对于一对点 ( i , j ) ( t i > t j ) (i,j)\space(t_i > t_j) (i,j) (ti>tj),如果从 j j j 在 t j t_j tj 时刻出发,能看到 i i i 的事件的条件
- 当
x
i
>
x
j
x_i>x_j
xi>xj 时
从 j j j 能赶到 i i i ⟺ \iff ⟺ − x j + v × t j ≤ − x i + v × t i -x_j+v\times t_j\le -x_i+v\times t_i −xj+v×tj≤−xi+v×ti - 当
x
i
<
x
j
x_i<x_j
xi<xj 时
从 j j j 能赶到 i i i ⟺ \iff ⟺ x j + v × t j ≤ x i + v × t i x_j+v\times t_j\le x_i+v\times t_i xj+v×tj≤xi+v×ti
发现一个问题,就是将上面两个柿子推导一下
- 当
x
i
>
x
j
x_i>x_j
xi>xj 时
从 j j j 能赶到 i i i ⟺ \iff ⟺ − x j + v × t j ≤ − x i + v × t i -x_j+v\times t_j\le -x_i+v\times t_i −xj+v×tj≤−xi+v×ti ⟺ \iff ⟺ 2 x j − x j + v × t j ≤ 2 x i − x i + v × t i 2x_j-x_j+v\times t_j\le 2x_i-x_i+v\times t_i 2xj−xj+v×tj≤2xi−xi+v×ti ⟺ \iff ⟺ x j + v × t j ≤ x i + v × t i x_j+v\times t_j\le x_i+v\times t_i xj+v×tj≤xi+v×ti - 当
x
i
<
x
j
x_i<x_j
xi<xj 时
从 j j j 能赶到 i i i ⟺ \iff ⟺ x j + v × t j ≤ x i + v × t i x_j+v\times t_j\le x_i+v\times t_i xj+v×tj≤xi+v×ti ⟺ \iff ⟺ x j − 2 x j + v × t j ≤ x i − 2 x i + v × t i x_j-2x_j+v\times t_j\le x_i-2x_i+v\times t_i xj−2xj+v×tj≤xi−2xi+v×ti ⟺ \iff ⟺ − x j + v × t j ≤ − x i + v × t i -x_j+v\times t_j\le -x_i+v\times t_i −xj+v×tj≤−xi+v×ti
也就是如果让
j
←
i
j \gets i
j←i 需要让上面两个柿子都成立
不妨设
a
i
=
−
x
i
+
v
×
t
i
,
b
i
=
x
i
+
v
×
t
i
a_i=-x_i+v\times t_i,b_i=x_i+v\times t_i
ai=−xi+v×ti,bi=xi+v×ti
如果按照
a
i
a_i
ai 排序的话,就变成了要找一个最长的子序列使得
b
i
b_i
bi 单调不降
问题就变成了
L
I
S
LIS
LIS
特别的,对于强制从原点出发的,因为
a
0
=
b
0
=
0
a_0=b_0=0
a0=b0=0,故所有
a
i
<
0
a_i<0
ai<0 或
b
i
<
0
b_i<0
bi<0 的点不能选,特判一下即可
CF76F代码
#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, V;
struct node{
int pos, times;
node(int times = 0,int pos = 0):pos(pos),times(times){}
friend bool operator < (node a,node b){return a.times != b.times ? a.times < b.times : a.pos < b.pos;}
}a[maxn];
struct node1{
int x, y;
node1(int x = 0,int y = 0):x(x),y(y){}
friend bool operator < (node1 a,node1 b){return a.x != b.x ? a.x < b.x : a.y < b.y;}
}b[maxn],tmp[maxn];
int x[maxn], y[maxn];
int que[maxn], r;
signed main(){
n = read();
for(int i = 1;i <= n;i++)a[i] = node(read(),read());
V = read();
int cnt = 0;
for(int i = 1;i <= n;i++){
b[i] = node1(-a[i].pos + V * a[i].times,a[i].pos + V * a[i].times);
x[i] = -a[i].pos + V * a[i].times; y[i] = a[i].pos + V * a[i].times;
if(b[i].x > 0 && b[i].y > 0){tmp[++cnt] = b[i];}
}
sort(x + 1,x + 1 + n);sort(y + 1,y + 1 + n);
for(int i = 1;i <= cnt;i++){tmp[i] = node1(lower_bound(x + 1,x + 1 + n,tmp[i].x) - x,lower_bound(y + 1,y + 1 + n,tmp[i].y) - y);}
sort(tmp + 1,tmp + 1 + cnt);
int ans1 = 0;r = 0;
que[0] = -1;que[++r] = 0x3f3f3f3f;
for(int i = 1;i <= cnt;i++){
int pos = upper_bound(que,que + r + 1,tmp[i].y) - que;
if(que[pos] > tmp[i].y)que[pos] = tmp[i].y;
if(pos >= r){que[++r] = 0x3f3f3f3f;}
}
ans1 = r - 1;
for(int i = 1;i <= n;i++){b[i] = node1(lower_bound(x + 1,x + 1 + n,b[i].x) - x,lower_bound(y + 1,y + 1 + n,b[i].y) - y);}
sort(b + 1,b + 1 + n);
int ans = 0;r = 0;
memset(que,0,sizeof(que));
que[0] = -1;que[++r] = 0x3f3f3f3f;
for(int i = 1;i <= n;i++){
int pos = upper_bound(que,que + r + 1,b[i].y) - que;
if(que[pos] > b[i].y)que[pos] = b[i].y;
if(pos >= r){que[++r] = 0x3f3f3f3f;}
}
ans = r - 1;
// for(int i = 1;i <= n;i++)printf("%d %d\n",b[i].x,b[i].y);
printf("%d %d\n",ans1,ans);
return 0;
}