CF311
CF311A
CF311A题意
给出一个伪代码:\
input n
for i from 1 to n
input the i-th point's coordinates into p[i]
sort array p[] by increasing of x coordinate first and increasing of y coordinate second
d=INF //here INF is a number big enough
tot=0
for i from 1 to n
for j from (i+1) to n
++tot
if (p[j].x-p[i].x>=d) then break //notice that "break" is only to be
//out of the loop "for j"
d=min(d,distance(p[i],p[j]))
output d
现在已知 n n n 和一个常数 k k k,希望你给出一个输入,使得 t o t > k tot>k tot>k。
CF311A题解
观察伪代码,发现这玩应的优化是横坐标差值大于当前答案就跳出,那我们就让构造的横坐标相等,纵坐标不同即可。
注意
t
o
t
tot
tot 是严格大于
k
k
k,当
n
×
(
n
−
1
)
2
≤
k
\frac{n\times(n-1)}{2}\le k
2n×(n−1)≤k 时无解。
CF311A代码
#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 = 2100;
int n, k;
signed main(){
n = read(); k = read();
if(n * (n - 1) / 2 <= k)puts("no solution");
else for(int i = 1;i <= n;i++){printf("0 %d\n",i);}
return 0;
}
CF311B
CF311B题意
Zxr960115 是一个大农场主。
他养了 m m m 只可爱的猫子,雇佣了 p p p 个铲屎官。这里有一条又直又长的道路穿过了农场,有 n n n 个山丘坐落在道路周围,编号自左往右从 1 1 1 到 n n n。山丘 i i i 与山丘 i − 1 i-1 i−1 的距离是 D i D_i Di 米。铲屎官们住在 1 1 1 号山丘。
一天,猫子们外出玩耍。猫子 i i i 去山丘 H i H_i Hi 游玩,在 T i T_i Ti 时间结束他的游玩,然后在山丘 H i H_i Hi 傻等铲屎官。铲屎官们必须把所有的猫子带上。每个铲屎官直接从 H 1 H_1 H1 走到 H n H_n Hn,中间不停下,可以认为不花费时间的把游玩结束的猫子带上。每个铲屎官的速度为一米每单位时间,并且足够强壮来带上任意数量的猫子。
举个栗子,假装我们有两个山丘( D 2 = 1 D_2=1 D2=1 ),有一只猫子,他想去山丘 2 2 2 玩到时间 3 3 3。然后铲屎官如果在时间 2 2 2 或者时间 3 3 3 从 1 1 1 号山丘出发,他就能抱走猫子。如果他在时间 1 1 1 出发那么就不行(猫子还在玩耍)。如果铲屎官在时间 2 2 2 出发,猫子就不用等他( Δ T = 0 \Delta T=0 ΔT=0)。如果他在时间 3 3 3 出发,猫子就要等他 1 1 1 个单位时间。
你的任务是安排每个铲屎官出发的时间(可以从 0 时刻之前出发),最小化猫子们等待的时间之和。
对于全部的数据,满足 2 ≤ n ≤ 1 0 5 2\le n\le10^5 2≤n≤105, 1 ≤ m ≤ 1 0 5 1\le m\le10^5 1≤m≤105, 1 ≤ p ≤ 100 1\le p\le100 1≤p≤100, 1 ≤ D i < 1 0 4 1\le D_i<10^4 1≤Di<104, 1 ≤ H i ≤ n 1\le H_i\le n 1≤Hi≤n, 0 ≤ t ≤ 1 0 9 0\le t\le10^9 0≤t≤109。
CF311B题解
不会斜率优化QAQ
首先想到这个
H
H
H 和
D
D
D 没啥用,直接预处理每只猫的最早出发时间
t
i
t_i
ti,然后
s
o
r
t
sort
sort 一下。
然后问题就变成让这
p
p
p 个铲屎官选择自己带几只猫出发。
首先想到每个铲屎官一定恰好在一只猫的最早时间出发,否则让这个铲屎官早点出发会更优。
然后考虑
D
P
DP
DP
设
f
i
,
j
f_{i,j}
fi,j 表示第
i
i
i 个铲屎官从第
j
j
j 只猫的最早出发时间出发的答案。
那么就有
f
i
,
j
=
min
k
=
1
j
−
1
(
f
i
−
1
,
k
−
s
u
m
j
+
s
u
m
k
+
(
j
−
k
)
×
t
j
)
f_{i,j}=\min_{k=1}^{j-1}(f_{i-1,k}-sum_j+sum_k+(j-k)\times t_j)
fi,j=mink=1j−1(fi−1,k−sumj+sumk+(j−k)×tj),其中
s
u
m
i
=
∑
j
=
1
i
t
j
sum_i=\sum_{j=1}^it_j
sumi=∑j=1itj
然后考虑转化下柿子,设满足上式的最小决策点是
k
k
k。
f
i
,
j
=
f
i
−
1
,
k
−
s
u
m
j
+
s
u
m
k
+
(
j
−
k
)
×
t
j
f_{i,j}=f_{i-1,k}-sum_j+sum_k+(j-k)\times t_j
fi,j=fi−1,k−sumj+sumk+(j−k)×tj
⇔
t
j
×
k
−
t
j
×
j
+
s
u
m
j
+
f
i
,
j
=
s
u
m
k
+
f
i
−
1
,
k
\Leftrightarrow t_j\times k-t_j\times j+sum_j+f_{i,j}=sum_k+f_{i-1,k}
⇔tj×k−tj×j+sumj+fi,j=sumk+fi−1,k
然后发现这玩应是
O
(
m
2
p
)
O(m^2p)
O(m2p),需要优化一个
m
m
m,而这玩应很像
k
⋅
x
+
b
=
y
k·x+b=y
k⋅x+b=y,似乎可以单调队列。
可是正确性我不会证(正确性交给读者证明
#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, maxp = 110;
int n, m, p;
int pre[maxn], t[maxn], sum[maxn];
int f[maxp][maxn], now;
int calcy(int x){return sum[x] + f[now][x];}
int calcb(int x,int k){return calcy(x) - t[k] * x;}
int cross(int a,int b,int c){
int x1__ = b - a, x2__ = c - b, y1__ = calcy(b) - calcy(a), y2__ = calcy(c) - calcy(b);
return x1__ * y2__ - x2__ * y1__;
}
int que[maxn], head, tail;
signed main(){
n = read(); m = read(); p = read();
for(int i = 2;i <= n;i++){pre[i] = pre[i - 1] + read();}
for(int i = 1;i <= m;i++){int H = read(),T = read();t[i] = T - pre[H];}
sort(t + 1,t + 1 + m);
for(int i = 1;i <= m;i++)sum[i] = sum[i - 1] + t[i];
for(int i = 1;i <= m;i++){f[1][i] = i * t[i] - sum[i];}
int ans = f[1][m];
for(int i = 2;i <= p;i++){
head = 1;tail = 0;now = i - 1;que[++tail] = 0;
for(int j = 1;j <= m;j++){
while(head < tail && calcb(que[head + 1],j) < calcb(que[head],j))head++;
int pos = que[head];f[i][j] = f[i - 1][pos] + (j - pos) * t[j] + sum[pos] - sum[j];
while(head < tail && cross(j,que[tail],que[tail - 1]) > 0)tail--;
que[++tail] = j;
}
ans = min(ans,f[i][m]);
}
printf("%lld\n",ans);
return 0;
}
CF311D
CF311D题意
你需要维护一个序列 a a a 并且需要支持以下两种操作:
- 1 l r 1\space l\space r 1 l r:将每一个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 区间中的数字 a i ← a i 3 a_i\gets a_i^3 ai←ai3。
- 2 l r 2\space l\space r 2 l r:询问区间 [ l , r ] [l,r] [l,r] 的总和。
答案对 95542721 取模(99542721是一个质数)。
CF311D题解
一道挺有意思的线段树和简单数论结合
如果修改为开三次方好做,但是修改为立方呢?
没啥思路…(一开始做题的时候就看翻译了,翻译中没有模数)
注意到 这个模数 好像不是 998244353,是个其他的东西。
这玩应有搞头吗?
如果这玩应有循环节,那么他就应该满足
∀
a
,
a
3
k
≡
a
(
m
o
d
95542721
)
\forall a,a^{3^k}≡a(mod\space 95542721)
∀a,a3k≡a(mod 95542721),其中
k
k
k 是一个常数。
然后你打下表(或者算下
3
k
≡
1
(
m
o
d
95542721
−
1
)
3^k≡1(mod\space 95542721 - 1)
3k≡1(mod 95542721−1)),发现
k
=
48
k=48
k=48
然后这玩应就可以线段树维护了。
具体而言,每个节点维护一个数组
s
u
m
0
,
1
,
.
.
.
,
47
sum_{0,1,...,47}
sum0,1,...,47 和一个循环位移标记
t
a
g
tag
tag
每次更新的时候把
t
a
g
←
t
a
g
+
1
tag\gets tag + 1
tag←tag+1 就完了。
不过需要注意的是,在询问下传标记的时候要在return答案之前在pushup回来。
CF311D代码
#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 = 95542721;
int n, m, a[maxn];
struct Segment_Tree{
struct node{
int sum[48], tag;
node(){memset(sum,0,sizeof(sum));tag = 0;}
node(int a){for(int i = 0;i < 48;i++){sum[i] = a % mod;a = a * a % mod * a % mod;}tag = 0;}
node(int a[],int b[],int l,int r){
for(int i = 0;i < 48;i++){sum[i] = (a[(i + l) % 48] + b[(i + r) % 48]) % mod;}tag = 0;}
}d[maxn << 2];
node mergenode(node l,node r){return node(l.sum,r.sum,l.tag,r.tag);}
void pushdown(int p){
if(d[p].tag){
d[p << 1].tag = (d[p << 1].tag + d[p].tag) % 48;
d[p << 1 | 1].tag = (d[p << 1 | 1].tag + d[p].tag) % 48;
d[p].tag = 0;
}
}
void build(int l = 1,int r = n,int p = 1){
if(l == r){d[p] = node(a[l]);return;}
int mid = l + r >> 1;
build(l,mid,p << 1);build(mid + 1,r,p << 1 | 1);
d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
}
void update(int l,int r,int s,int t,int p){
if(s <= l && r <= t){d[p].tag = (d[p].tag + 1) % 48;return;}
pushdown(p); int mid = l + r >> 1;
if(s <= mid)update(l,mid,s,t,p << 1);
if(mid < t)update(mid + 1,r,s,t,p << 1 | 1);
d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
}
// node query(int l,int r,int s,int t,int p){
// if(s <= l && r <= t)return d[p];
// pushdown(p); int mid = l + r >> 1;
// if(t <= mid)return query(l,mid,s,t,p << 1);
// if(mid < s)return query(mid + 1,r,s,t,p << 1 | 1);
// return mergenode(query(l,mid,s,t,p << 1),query(mid + 1,r,s,t,p << 1 | 1));
// }
// int query(int l,int r){node tmp = query(1,n,l,r,1);return tmp.sum[tmp.tag] % mod;}
int query(int l,int r,int s,int t,int p){
if(s <= l && r <= t)return d[p].sum[d[p].tag % 48] % mod;
pushdown(p); int mid = l + r >> 1;d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
if(t <= mid)return query(l,mid,s,t,p << 1);
if(mid < s)return query(mid + 1,r,s,t,p << 1 | 1);
return (query(l,mid,s,t,p << 1) + query(mid + 1,r,s,t,p << 1 | 1)) % mod;
}
int query(int l,int r){return query(1,n,l,r,1) % mod;}
void update(int l,int r){update(1,n,l,r,1);}
}tree;
signed main(){
n = read();for(int i = 1;i <= n;i++)a[i] = read() % mod;
int opt, l, r; m = read();tree.build();
for(int j = 1;j <= m;j++){
opt = read(); l = read(); r = read();
if(opt == 1){printf("%lld\n",tree.query(l,r));}
else tree.update(l,r);
// for(int i = 1;i <= n;i++){printf("a[%lld] = %lld\n",i,tree.query(i,i));}
}
return 0;
}
CF311E
CF311E题意
给定
n
n
n 个点和一个常数
g
g
g,设每个点有开(
1
1
1)和关(
0
0
0)两种状态。
给定每个点
i
i
i 的初始状态
a
i
a_i
ai,和改变点状态的代价
v
i
v_i
vi。
有
m
m
m 种要求,每种要求
i
i
i 需要所有的
k
i
k_i
ki 个点
i
d
i
,
1
,
i
d
i
,
2
,
.
.
.
,
i
d
i
,
k
i
id_{i,1},id_{i,2},...,id_{i,k_i}
idi,1,idi,2,...,idi,ki 均为状态
s
t
a
i
(
=
0
/
1
)
sta_i(=0/1)
stai(=0/1),如果达成了就会得到
w
i
w_i
wi 的收益,如果没达成则需要另外付出
g
×
t
y
p
i
g\times typ_i
g×typi (
t
y
p
i
=
0
/
1
typ_i=0/1
typi=0/1)的代价(设为
c
o
s
t
i
cost_i
costi)。
问你能获得收益的最大值。
n
≤
1
0
4
,
m
≤
1000
,
k
i
≤
10
n\le10^4,m\le1000,k_i\le10
n≤104,m≤1000,ki≤10
CF311E题解
最大权闭合子图+最小割。
首先,这么多的限制似乎很像网络流状物。
然后再仔细看看题干,发现如果第
i
i
i 种要求满足,那么他所有的管辖点一定全都是这种状态,每种要求和点都有自己的状态。
最大权闭合子图!
考虑如何建图。
设源点
S
S
S 和汇点
T
T
T,那么让
S
→
i
S\to i
S→i,边权是
a
i
×
v
i
a_i\times v_i
ai×vi,并让
i
→
T
i\to T
i→T,边权是
(
a
i
=
=
0
)
×
v
i
(a_i == 0)\times v_i
(ai==0)×vi。
然后对于每个条件
j
j
j:
- 如果
j
j
j 要求所有点是
0
0
0:
- 将 S → j S\to j S→j,边权是 c o s t j + w j cost_j+w_j costj+wj
- 将 j → i d j , 1 , 2 , . . . , k j j\to id_{j,1,2,...,k_j} j→idj,1,2,...,kj,边权是 I N F INF INF
- 如果
j
j
j 要求所有点是
0
0
0:
- 将 j → T j\to T j→T,边权是 c o s t j + w j cost_j+w_j costj+wj
- 将 i d j , 1 , 2 , . . . , k j → j id_{j,1,2,...,k_j}\to j idj,1,2,...,kj→j,边权是 I N F INF INF
然后跑最小割,得到
m
a
x
f
l
o
w
maxflow
maxflow,答案就是
∑
i
=
1
m
w
i
−
m
a
x
f
l
o
w
\sum_{i=1}^mw_i-maxflow
∑i=1mwi−maxflow。
没了。
CF331E代码
#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, INF = 0x3f3f3f3f;
int n, m, g;
int a[maxn], val[maxn];
int head[maxn], tot = 1;
struct edge{
int to, nexte,cap, flow;
edge(int to =0,int ne = 0,int ca = 0
,int fl = 0):to(to),nexte(ne),cap(ca),flow(fl){}
}e[maxn << 1];
void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;}
void addd(int u,int v,int cap){add(u, v, cap);add(v, u, 0);}
queue<int> que;int dis[maxn], cur[maxn];
int dfs(int u,int flow,int T){
if(u == T || !flow)return flow;
int res = 0;
for(int i = head[u];i;i = e[i].nexte){
int v = e[i].to;cur[u] = i;
if(e[i].cap > e[i].flow && dis[v] == dis[u] + 1){
int tmp = dfs(v,min(flow,e[i].cap - e[i].flow),T);
if(!tmp)cur[v] = 0;e[i ^ 1].flow -= tmp;
res += tmp;flow -= tmp;e[i].flow += tmp;
}
}
return res;
}
bool bfs(int S,int T){
memset(cur,0,sizeof(cur));memset(dis,0x3f,sizeof(dis));
while(!que.empty())que.pop();
que.push(S);dis[S] = 0;cur[S] = head[S];
while(!que.empty()){
int u = que.front();que.pop();
for(int i = head[u];i;i = e[i].nexte){
int v = e[i].to;
if(dis[v] == INF && e[i].cap > e[i].flow){
dis[v] = dis[u] + 1; cur[v] = head[v];
que.push(v);
}
}
}
return dis[T] != INF;
}
int Dinic(int S,int T){
int mxflow = 0;
while(bfs(S,T))mxflow += dfs(S,INF,T);
return mxflow;
}
signed main(){
n = read(); m = read(); g = read();
int S = n + m + 2, T = n + m + 3, sum = 0;
for(int i = 1;i <= n;i++){a[i] = read();}
for(int i = 1;i <= n;i++){
val[i] = read();
if(!a[i]){addd(S,i,val[i]);}
else{addd(i,T,val[i]);}
}
for(int i = 1;i <= m;i++){
int typ = read(), w = read(), k = read();sum += w;
for(int j = 1;j <= k;j++){
if(!typ){addd(i + n,read(),INF);}
else addd(read(),i + n,INF);
}
int co = g * read();
if(!typ){addd(S,i + n,co + w);}
else addd(i + n,T,co + w);
}
int ans = Dinic(S,T);
printf("%d\n",sum - ans);
return 0;
}