首页 > 其他分享 >CF1428D Bouncing Boomerangs 题解

CF1428D Bouncing Boomerangs 题解

时间:2024-02-07 10:15:53浏览次数:38  
标签:Boomerangs int 队列 题解 re 枚举 CF1428D 从左往右 define

解题思路

很简单的贪心。

观察发现以下性质:

  • 当 \(a_i=2\) 时,这一行一定只有两个目标,且第二个目标一定位于一个 \(a_j=1\) 的格子内;
  • 当 \(a_i=3\),那么当前列右边某一列发生转向的地方,\(a_j\not=0\);

那么这道题就基本已经做出来了。因为 \(a_i=3\) 的格子转向格可以在任意非 \(0\) 列,而 \(a_i=2\) 的转向格一定在 \(a_j=1\) 列,那么我们考虑优先满足 \(a_i=2\) 的列的需求。显然,不管怎么填,占用的行数是固定的,那么我们考虑第 \(i\) 列填在第 \(i\) 行,这样可以有效避免一些重复。接下来我们填 \(a_i=3\) 的格子,贪心的从左往右找到第一个可以有转向格的列,然后直接填就可以了。最后把 \(a_i=1\) 的补上就结束了。

注意,我们填的时候不需要考虑有没有填过,直接无脑填就可以了,最后再去重。

综上,本题步骤如下:

  • 从左往右找到所有 \(a_i=1\) 的列,并按顺序放入一个队列中;
  • 从左往右枚举 \(a_i=2\) 的列,并不断弹出队首直到队首列在当前列右侧,然后在队首列填入一个目标,并标记为已填;
  • 清空队列,从左往右枚举所有非 \(0\) 列,并放入队列中;
  • 从左往右枚举 \(a_i=3\) 的列,找到第一个在当前列右侧的列,然后填入两个目标;
  • 枚举剩余的没有被填过的 \(a_i=1\) 的列,向其中填入一个目标;
  • 排序去重输出答案。

注意事项

  • 在维护可填列的时候一定要使用队列来维护,贪心地从左往右填;
  • 注意纵坐标是从上往下递增的,而不是从下往上。

AC 代码

应该是比较短的代码了吧……

#include<bits/stdc++.h>
#define N 100005
#define p push_back
#define C continue
#define re register
#define pii std::pair<int,int>
std::vector<pii> A;
int n,a[N],vis[N];
signed main(){scanf("%d",&n);
    for(re int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    std::queue<int> q;
    for(re int i=1;i<=n;++i)
        if(a[i]==1) q.push(i);
    for(re int i=1;i<=n;++i){
        if(a[i]!=2) C;
        while(q.size()&&q.front()<i) q.pop();
        if(q.empty()){puts("-1");return 0;}
        A.p({i,i});
        A.p({i,q.front()});
        vis[q.front()]=1;q.pop();
    }while(q.size()) q.pop();
    for(re int i=1;i<=n;++i){
        if(a[i]!=0&&!vis[i]) q.push(i);
    }for(re int i=1;i<=n;++i){
        if(a[i]!=3) C;
        while(q.size()&&q.front()<=i) q.pop();
        if(q.empty()){puts("-1");return 0;}
        A.p({i,i});A.p({i,q.front()});
        A.p({q.front(),q.front()});
        vis[q.front()]=1;q.pop();
    }for(re int i=1;i<=n;++i){
        if(a[i]!=1) C;
        if(vis[i]) C;A.p({i,i});
    }std::sort(A.begin(),A.end());
    int res=std::unique(A.begin(),A.end())-A.begin();
    printf("%d\n",res);
    for(re int i=0;i<res;++i){
        printf("%d ",A[i].first);
        printf("%d\n",A[i].second);
    }
}

标签:Boomerangs,int,队列,题解,re,枚举,CF1428D,从左往右,define
From: https://www.cnblogs.com/UncleSamDied6/p/18010657

相关文章

  • CF1401E Divide Square 题解
    解题思路其实多看几组也能发现块数等于交点的数量加上两个端点都在边上的线段数量再加一。证明如下(图见样例):对于两条只有一个端点位于边上的线段,因为保证有一个端点位于边上,那么这两条线段的交点一定会和已存在的点、边构成一个新的矩形;对于其中有一条为两个端点均位于边上的......
  • CF1401F Reverse and Swap 题解
    解题思路巧妙的数据结构题,非常巧妙的利用的线段树的奇妙性质。操作逐条维护:Replace:直接线段树上单点修改;Sum:线段树查询区间和;Reverse:考虑线段树的形态。线段树第\(i\)层(除最后一层)有\(2^{i-1}\)个节点,那么将所有\(i\ge1\)的区间\([(i-1)\times2^k,i\times2^k]\)......
  • CF1408E Avoid Rainbow Cycles 题解
    解题思路第一眼看过去感觉不是很可做……但是我们可以发现,如果有两个点在不同的集合中出现过,那么一定会存在彩虹环,那么两个点最多出现一次。同时我们考虑将题意转化一下,变成求最大能选取的点,使得不出现彩虹环。根据刚刚的性质,我们可以考虑每个点向它所在的集合连一条边权为\(a_......
  • CF1338C Perfect Triples 题解
    解题思路没什么好说的,就是打表找规律……表在这里不难发现,三元组中第一个数的最后两位按照\(00\to01\to10\to11\)的顺序变化,其他位也一样,同样,第二个数和第三个数中每两位分别按照\(00\to10\to11\to01\)和\(00\to11\to01\to10\)的顺序变化,且与第一个数对应变化......
  • CF1379C Choosing flowers 题解
    解题思路不是那么显然的,当只选一种\(b_i\)或全选\(a_i\)时最优。那么我们可以先对\(a_i\)从大到小排序,枚举每一个\(b_i\),然后二分找到第一个大于等于\(b_i\)的\(a_j\),判断\(a_1\sima_j\)中是否包含\(a_i\),如果包含,当前的答案为\(\displaystyle\left(\sum_{k=1}^{......
  • CF1385F Removing Leaves 题解
    解题思路简单贪心,优先选择叶子节点最多的,这样能够保证一定能把所有能删的都删了。因为要建一个可删除的图,所以我们可以使用set来存边,不然就需要维护一堆东西……那么我们肯定是从有叶子节点的点向父亲更新的,那么我们每次选择叶子节点最多的点,然后删除\(k\)个叶子,判断一下删......
  • CF1415E New Game Plus! 题解
    解题思路简单贪心题,我们可以把整个序列看作\(k+1\)个可重集,首先可以得到一个显然的结论:较大的数一定比较小的数先放入一个集合中。同样,由于每一轮\(ans\getsans+\maxsum_i\),其中\(sum_i\)表示第\(i\)个集合的元素和,那么,我们一定会将当前的元素放入当前和最大的哪个集合......
  • CF1416B Make Them Equal 题解
    解题思路观察可以发现,每次操作后序列元素之和不变,那么我们可以将每一次操作看作是\(a_i\)向\(a_j\)移动了\(i\timesx\)。由此可得,若序列和\(sum\bmodn\not=0\),那么一定无解,否则一定存在一个合法的操作方案。因为每次移动时移动的数应为\(i\)的倍数,所以\(a_1\)可以向......
  • CF1446C Xor Tree 题解
    解题思路与其考虑删除哪些点,不如考虑保留哪些点。考虑到和异或有关,那么我们可以把这些数倒序插入trie树中,然后我们就可以在trie树上跑一个简单的dp:若当前节点为叶子节点,那么保留,返回\(1\);若当前节点在链上,那么直接继承儿子节点;若当前节点有两个儿子,那么更新为较大儿子......
  • CF1922E Increasing Subsequences 题解
    解题思路因为可以有空集,那么我们首先构造第一段最长的连续上升的序列,那么这段序列中共有\(2^{\mids\mid}\)个上升子序列。接下来我们考虑补全剩余的,我们不妨将剩余的部分全部设为连续不增序列,那么设当前位置在第一段中有\(k\)个小于它的,那么添加这个数后可以增加\(2^{k-1}......