CF693Div3-F,G

前言:

这次DIV3还是蛮简单的

F.题目大意:

给你一个 2 × N 2 \times N 2×N大小的棋盘,有 m m m个点被禁止。问你是否能够放 1 × 2 和 2 × 1 1 \times 2 和 2 \times 1 1×22×1将棋盘填满.

题目思路:

考虑填满,条件很苛刻。不妨从前往后看:

最开始一定是竖着放。比横着放好。

直到遇到第一个障碍,找规律后不难发现它一定是要与后面的那个障碍成对组成一段满的.(从前往后两两配对.)

而两个障碍若在同一行则距离必须为偶数,反之必须为奇数.这样才能填满.

我们用 m a t c h [ i ] match[i] match[i]来标志 第 i i i 个方块是否配对.对方块排序后 O ( m ) O(m) O(m)扫一遍两两配对即可.

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
pii a[maxn];
int match[maxn];
int main()
{
    ios::sync_with_stdio(false);
    int t; cin >> t;
    while (t--){
        int n , k; cin >> k >> n;
        for (int i = 1 ; i <= n ; i++){
            int x , y; cin >> x >> y;
            a[i].first = y;
            a[i].second = x;
            match[i] = 0;
        }
        if (n == 1){
            cout << "NO" << endl;
            continue;
        }
        sort(a + 1 , a + 1 + n);
        for (int i = 2 ; i <= n ; i++){
            if (a[i].first == a[i - 1].first)
                match[i] = match[i - 1] = 1;
        }
        for (int i = 2 ; i <= n ; i ++){
            if (match[i]) continue;
            if (match[i - 1]) continue;
            if (a[i].second != a[i - 1].second){
                if ((a[i].first - a[i - 1].first) % 2 == 0)
                    match[i] = match[i - 1] = 1;
            }
            else {
                if ((a[i].first - a[i - 1].first) % 2)
                    match[i] = match[i - 1] = 1;
            }
        }
        bool ok = true;
        for (int i = 1 ; i <= n ; i++)
            if (!match[i]) ok = false;
        if (ok) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}
G.题目大意:

给你一个有向无权图。令 d i d_i di为从 1 1 1节点到 i i i节点的最短路.对于图中每个点,求出以下条件限制下能够达到的具有最小 d j d_j dj的值.

条件:在 i i i点上,可以任意次的往 d d d增大的方向走. 或者只能最多走一次,往 d d d非递增的方向走一步.

题目思路:

先BFS求出 d i s t dist dist数组。考虑其生成的以1为根的bfs外向生成树.

考虑一个点 i i i最终答案能比 d i d_i di小的条件:

1.直接和它相连的返祖边使得到达 j j j,其 d j ≤ d i d_j \leq d_i djdi.(这时候只能停止或者往 d d d增大的方向走,所以 d j d_j dj是最优的)

2.没有返祖边,但是它能够通过生成树到达 1这种点,那么可以更新这个点的答案.

(其实就是所有返祖边导致成环,那么环里的点都能被更新)

所以bfs后我们可以用一次dfs来更新答案.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
vector<int> e[maxn];
int d[maxn][2];
void dfs (int u)
{
    for (auto v : e[u]){
        if (d[u][0] >= d[v][0])
            d[u][1] = min(d[u][1] , d[v][0]);
        else {
            dfs(v);
            d[u][1] = min (d[u][1] , d[v][1]);
        }
    }
    return ;
}
int main()
{
    ios::sync_with_stdio(false);
    int t; cin >> t;
    while (t--){
        int n , m; cin >> n >> m;
        for (int i = 1 ; i <= n ; i++){
            e[i].clear();
            d[i][0] = d[i][1] = 1e9;
        }
        for (int i = 1 ; i <= m ; i++){
            int x , y; cin >> x >> y;
            e[x].pb (y);
        }
        queue<int> q;
        q.push(1);
        d[1][0] = 0;
        while (q.size()){
            int g = q.front();q.pop();
            for (auto v : e[g])
                if (d[v][0] == 1e9)
                    d[v][0] = d[g][0] + 1 , q.push(v);
        }
        dfs(1);
        for (int i = 1 ; i <= n ; i++)
            cout << min(d[i][0],d[i][1]) << " ";
        cout << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值