CF311

CF311

CF311A

link

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×(n1)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

link

CF311B题意

Zxr960115 是一个大农场主。

他养了 m m m 只可爱的猫子,雇佣了 p p p 个铲屎官。这里有一条又直又长的道路穿过了农场,有 n n n 个山丘坐落在道路周围,编号自左往右从 1 1 1 n n n。山丘 i i i 与山丘 i − 1 i-1 i1 的距离是 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 2n105 1 ≤ m ≤ 1 0 5 1\le m\le10^5 1m105 1 ≤ p ≤ 100 1\le p\le100 1p100 1 ≤ D i < 1 0 4 1\le D_i<10^4 1Di<104 1 ≤ H i ≤ n 1\le H_i\le n 1Hin 0 ≤ t ≤ 1 0 9 0\le t\le10^9 0t109

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=1j1(fi1,ksumj+sumk+(jk)×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=fi1,ksumj+sumk+(jk)×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×ktj×j+sumj+fi,j=sumk+fi1,k
然后发现这玩应是 O ( m 2 p ) O(m^2p) O(m2p),需要优化一个 m m m,而这玩应很像 k ⋅ x + b = y k·x+b=y kx+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

link

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 aiai3
  • 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,a3ka(mod 95542721),其中 k k k 是一个常数。
然后你打下表(或者算下 3 k ≡ 1 ( m o d   95542721 − 1 ) 3^k≡1(mod\space 95542721 - 1) 3k1(mod 955427211)),发现 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 tagtag+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

link

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 n104,m1000,ki10

CF311E题解

最大权闭合子图+最小割。
首先,这么多的限制似乎很像网络流状物。
然后再仔细看看题干,发现如果第 i i i 种要求满足,那么他所有的管辖点一定全都是这种状态,每种要求和点都有自己的状态。
最大权闭合子图!
考虑如何建图。
设源点 S S S 和汇点 T T T,那么让 S → i S\to i Si,边权是 a i × v i a_i\times v_i ai×vi,并让 i → T i\to T iT,边权是 ( 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 Sj,边权是 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} jidj,1,2,...,kj,边权是 I N F INF INF
  • 如果 j j j 要求所有点是 0 0 0
    • j → T j\to T jT,边权是 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,...,kjj,边权是 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=1mwimaxflow
没了。

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;
}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值