CF1427

CF1427

CF1427A

link

CF1427A题意

给出一个数组 a a a,要求你改变它的元素顺序,使得任意一个前缀和不为 0 0 0,无解输出 N O NO NO,有解给出顺序

CF1427A题解

算下总和 s u m sum sum

  • s u m = 0 sum=0 sum=0: 无解
  • s u m < 0 sum<0 sum<0:让负数在前面,正数在后面,一定使得前缀和小于 0 0 0
  • s u m > 0 sum>0 sum>0:让正数在前面,负数在后面,一定使得前缀和大于 0 0 0

CF1427A代码

#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 = 51;
int T;vector<int> vec;
signed main(){
    T = read();
while(T--){
    int n = read();vec.clear();int sum = 0;
    for(int i = 1;i <= n;i++){vec.push_back(read());}
    for(int i : vec){sum += i;}
    if(sum == 0){puts("NO");continue;}
    if(sum > 0)sort(vec.begin(),vec.end(),greater<int>());
    else sort(vec.begin(),vec.end(),less<int>());
    puts("YES");for(int i : vec){printf("%d ",i);}puts("");
}
    return 0;
}

CF1427B

link

CF1427B题意

有一个字符串,对于每一位字符 s i s_i si

  • s i = L s_i=L si=L:贡献是零
  • s i = W s_i=W si=W:
    • s i − 1 = L s_{i-1}=L si1=L:贡献是一
    • s i − 1 = W s_{i-1}=W si1=W:贡献是二

现在你有 k k k 次修改机会,问最大贡献是多少。

CF1427B题解

一个显而易见的结论是,最优方式一定是将 L → W L\to W LW
然后考虑怎么改。
设一共有 c n t cnt cnt W W W,连续的 W W W 段有 x x x 个,那么答案就是 ( c n t + k ) × 2 − x (cnt + k)\times2-x (cnt+k)×2x
现在想让 x x x 尽可能小。
贪心的想,尽可能的将两个相距“近”的连续段连在一起,这样就能使得答案尽可能大。
然后就没了。

CF1427B代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, k;
vector<int> vec;
char ch[maxn];bool book[maxn];
signed main(){
int T;scanf("%d",&T);
while(T--){
    scanf("%d%d",&n,&k);scanf("%s",ch + 1);
    bool pre = false;int cnt = 0,len = 0;vec.clear();
    for(int i = 1;i <= n;i++){
        if(ch[i] == 'W'){pre = true;cnt++;}
        if(ch[i] == 'L' && pre){len++;}
        else{
            if(len)vec.push_back(len),len = 0;
        }
    }
    if(cnt + k >= n){printf("%d\n",n * 2 - 1);continue;}
    sort(vec.begin(),vec.end());
    int tmp = 0, x = 0;
    for(int i = 0;i < vec.size();i++){
        tmp += vec[i];
        if(tmp > k){
            x = vec.size() - i + 1;
            break;
        }
    }
    if(k >= tmp && (k || cnt)){x = 1;}
    printf("%d\n",(cnt + k) * 2 - x);
}
    return 0;
}

CF1427C

link

CF1427C题意

有一个 r × r ( r ≤ 500 ) r\times r(r\le 500) r×r(r500) 的矩阵,其中有 n ( n ≤ 1 0 5 ) n(n\le10^5) n(n105) 个事件,坐标是 ( x i , y i ) (x_i,y_i) (xi,yi),发生时间是 t i t_i ti,所有事件只能在发生瞬间看到。
你初始在 ( 1 , 1 ) (1,1) (1,1),问最多能看到多少个事件?

CF1427C题解

先考虑一个朴素DP
d p i = max ⁡ ( d p j + ( ∣ x i − x j ∣ + ∣ y i − y j ∣ ≤ t i − t j ) ) dp_i=\max(dp_j+(|x_i-x_j|+|y_i-y_j|\le t_i-t_j)) dpi=max(dpj+(xixj+yiyjtitj))
不难发现这个是 O ( n 2 ) O(n^2) O(n2) 的,直接T飞了
发现这个 r r r 很小,并且第 i i i个点,所有的 t j ≤ t i − 2 ∗ r t_j\le t_i-2*r tjti2r 都一定能从 j j j 赶到 i i i
然后就没了。

CF1427C代码

#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,maxr = 510;
int n, r;
int x[maxn], y[maxn], t[maxn];
int f[maxn];
signed main(){
    r = read(); n = read();x[0] = y[0] = 1;t[0] = 0;
    memset(f,-0x3f,sizeof(f));f[0] = 0;
    for(int i = 1;i <= n;i++){t[i] = read(); x[i] = read(); y[i] = read();}
    int mx = -0x3f3f3f3f;
    for(int i = 1;i <= n;i++){
        f[i] = max(mx + 1,f[i]);
        for(int j = max(0,i - 2 * r);j < i;j++)
            if(abs(x[i] - x[j]) + abs(y[i] - y[j]) <= t[i] - t[j])
                f[i] = max(f[i],f[j] + 1);
        if(i >= r * 2) mx = max(mx,f[i - r * 2]);
    }
    int ans = 0;
    for(int i = 1;i <= n;i++)ans = max(ans,f[i]);
    printf("%d\n",ans);
    return 0;
}

CF1427F

link

CF1427F题意

6 n 6n 6n 张牌,顺序排列,两人轮流操作,每次操作可以选择连续的三张牌放入自己的口袋中,给出第一个人最后得到的牌,构造合法的操作顺序,使得第一个人最后得到的牌为输入给定的牌。

CF1427F题解

这套题怎么这么多构造题?!
首先先考虑一个问题,就是如果没有交替操作的限制怎么做。
简单啊,维护一个 s t a c k stack stack,每次将第 i i i 张牌压入栈中,如果连续三个就 p o p pop pop
但是现在加上这个限制咋整?
不难发现,如果将上面的 p o p pop pop 加一些改变,在 p o p pop pop 的同时分别以“(”,“)”标记下左右端点,这个序列就变成了括号序列,其中想要删除某个括号序列,就必须删除它里面的所有序列。
具体的说,如果有一个 p o p pop pop,那就让这个被 p o p pop pop 的“块”向现在的栈顶连有向边。
如果按照这个关系连边…

这玩应不是一堆树(森林)吗?(反正是 D A G DAG DAG
然后快乐的发现这玩应可以 d e q u e deque deque,具体的说,每次弹出入度为零的点,然后如果更新新的点可以入队,就按照颜色分别向左右端压入。
然后就完了~~~~~~~吗?
Wrong answer on test 2
The sequence of moves produces a different set of cards in Federico’s pocket.
然后你手玩了下 test 2 的输出。
woc第二个人拿的你拿什么?!(怒
突然想到一个问题,如果现在所有颜色是零的点入度都不是零咋整?
突然发现这种问题是由于自己取的方式不对。
比如说这个情况 1 ← 0 , 0 1\gets 0,0 10,0 现在要取颜色是零的点。
然后你的程序快乐的取了左面的零,然后到颜色是一的时候傻了。

  • “这也没有颜色是一,入度是零的点啊?”
  • “算了反正取一个就好了我又不是做题的那个,我只是个程序。”

你TM演我?!
于是让程序聪明点,每次让程序尽可能取可以减少入度的点,实在不行在取不能减少入度的点。
这下就真没啦。

CF1427F代码

#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 = 1300;
struct block{
    vector<int> vec;
    int id, col;
    block(int col = 0,int id = 0):col(col),id(id){}
    void ins(int x){this->vec.push_back(x);}
}b[maxn];
int tot, n, fa[maxn];
int ind[maxn];
stack<block> stk;
bool book[maxn];
stack<int> que,que1,que2;
signed main(){
    n = read() * 3;
    for(int i = 1;i <= n;i++){book[read()] = 1;}n *= 2;
    for(int i = 1;i <= n;i++){
        if(stk.empty() || stk.top().col != book[i]){
            stk.push(block(book[i],++tot));
            stk.top().ins(i);b[tot] = stk.top();
        }
        else{
            stk.top().ins(i);b[stk.top().id].ins(i);
            if(stk.top().vec.size() >= 3){
                block tmp = stk.top();stk.pop();
                if(stk.empty())continue;
                fa[tmp.id] = stk.top().id;
                ind[stk.top().id]++;
            }
        }
    }
    for(int i = 1;i <= tot;i++){
        if(!ind[b[i].id]){
            if(b[i].col == 1){que.push(b[i].id);}
            else{
                if(!fa[b[i].id])que2.push(b[i].id);
                else que1.push(b[i].id);
            }
        }
    }
    int now = 1,cnt = 0;n /= 3;
    while(cnt < n){
        block tmp = block();
        if(now){tmp = b[que.top()];que.pop();}
        else{
            if(!que1.empty()){tmp = b[que1.top()];que1.pop();}
            else {tmp = b[que2.top()];que2.pop();}
        }
        for(int i : tmp.vec){printf("%d ",i);}puts("");
        ind[fa[tmp.id]]--;
        if(!ind[fa[tmp.id]]){
            if(b[fa[tmp.id]].col == 1){que.push(b[fa[tmp.id]].id);}
            else{
                if(!fa[b[fa[tmp.id]].id]){que2.push(b[fa[tmp.id]].id);}
                else que1.push(b[fa[tmp.id]].id);
            }
        }
        now ^= 1;cnt++;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值