結果
問題 | No.274 The Wall |
ユーザー |
![]() |
提出日時 | 2020-07-04 22:30:49 |
言語 | C++14 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 632 ms / 2,000 ms |
コード長 | 6,919 bytes |
コンパイル時間 | 2,185 ms |
コンパイル使用メモリ | 181,624 KB |
実行使用メモリ | 260,224 KB |
最終ジャッジ日時 | 2024-06-22 02:37:55 |
合計ジャッジ時間 | 3,857 ms |
ジャッジサーバーID (参考情報) |
judge2 / judge4 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 4 |
other | AC * 22 |
ソースコード
#include <bits/stdc++.h>using namespace std;using ll = long long;using ld = long double;using pint = pair<int, int>;using pll = pair<ll, ll>;using pld = pair<ld, ld>;const int INF=1e9+7;const ll LINF=9223372036854775807;const ll MOD=1e9+7;const ld PI=acos(-1);const ld EPS = 1e-10; //微調整用(EPSより小さいと0と判定など)int ii() { int x; if (scanf("%d", &x)==1) return x; else return 0; }long long il() { long long x; if (scanf("%lld", &x)==1) return x; else return 0; }string is() { string x; cin >> x; return x; }char ic() { char x; cin >> x; return x; }void oi(int x) { printf("%d ", x); }void ol(long long x) { printf("%lld ", x); }void od_nosp(double x) { printf("%.15f", x); } // 古い問題用void od(double x) { printf("%.15f ", x); }// long doubleで受け取り、fをLfなどに変えて出力すると、変な数値が出る// それをなんとかするには独自の出力を作らなければならなそうvoid os(const string &s) { printf("%s ", s.c_str()); }void oc(const char &c) { printf("%c ", c); }#define o_map(v){cerr << #v << endl; for(const auto& xxx: v){cout << xxx.first << " " << xxx.second << "\n";}} //動作未確認void br() { putchar('\n'); }// #define gcd __gcd //llは受け取らない C++17~のgcdと違うので注意// int lcm(int a, int b){return a / gcd(a, b) * b;}#define begin_end(a) a.begin(),a.end() //sort(begin_end(vec));#define REP(i,m,n) for(ll i=(ll)(m) ; i < (ll) (n) ; i++ )#define rep(i,n) REP(i,0,n)#define m_p(a,b) make_pair(a,b)#define SORT_UNIQUE(c) (sort(c.begin(),c.end()), c.resize(distance(c.begin(),unique(c.begin(),c.end()))))#define GET_POS(c,x) (lower_bound(c.begin(),c.end(),x)-c.begin())#define p_b push_back#define SZ(x) ((int)(x).size) //size()がunsignedなのでエラー避けに// coutによるpairの出力(空白区切り)template<typename T1, typename T2> ostream& operator<<(ostream& s, const pair<T1, T2>& p) {return s << "(" << p.first << " " << p.second << ")";}// coutによるvectorの出力(空白区切り)template<typename T> ostream& operator<<(ostream& s, const vector<T>& v) {int len = v.size();for (int i = 0; i < len; ++i) {s << v[i]; if (i < len - 1) s << " "; //"\t"に変えるとTabで見やすく区切る}return s;}// coutによる多次元vectorの出力(空白区切り)template<typename T> ostream& operator<<(ostream& s, const vector< vector<T> >& vv) {int len = vv.size();for (int i = 0; i < len; ++i) {s << vv[i] << endl;}return s;}//最大値、最小値の更新。更新したor等しければtrueを返すtemplate<typename T>bool chmax(T& a, T b){return (a = max(a, b)) == b;}template<typename T>bool chmin(T& a, T b){return (a = min(a, b)) == b;}//4近傍(上下左右) rep(i, 2) にすると右・下だけに進むvector<int> dx_4 = {1, 0, -1, 0};vector<int> dy_4 = {0, 1, 0, -1};// -------- template end - //// - library ------------- //// 2-SAT// (出典)https://kopricky.github.io/code/Computation_Advanced/two_sat.html// (a∨b) ∧ (¬b∨c) ∧ … といった、各クロージャ内のリテラル数が高々2個であるようなな命題論理式(2-SAT)の充足可能性を判定// satisfy() が true(充足可能) であれば、vector<ll> ans に、与えられた2-SATを充足するような 各リテラル[i] の 真偽(1or0)を格納する// 各リテラル Xi と その否定 ¬Xi を頂点と考えて、強連結成分分解によって解く。// O(リテラル数 + クロージャ数)// なお、¬a∨¬b は 「aとbは両立しない」と言い換えられる。// 参考 : https://yukicoder.me/problems/no/274// 使い方// ll n = リテラル数;// TwoSAT two_sat(n);// - ※a,¬a,b,¬b,c,¬c があるとき、n = 3 。頂点倍加はライブラリ内で行う。// for(各クロージャ) add_closure(x, y); // これは クロージャ (x∨y) の例// - 否定(¬xや¬y)を入れたい時は +n する。ex) (¬x∨y) -> (x+n, y);// if (two_sat.satisfy()) rep(i, n) cout << two_sat.ans[i] << endl;// else cout << "impossible" << endl;class TwoSAT {private:const ll V;vector<vector<ll> > G, rG;vector<ll> ps, cmp;void add_edge(ll from, ll to){G[from].push_back(to), rG[to].push_back(from);}void dfs(ll u){cmp[u] = 0;for(ll v : G[u]){if(cmp[v] == -1) dfs(v);}ps.push_back(u);}void rdfs(ll u, ll k){cmp[u] = k;for(ll v : rG[u]){if(cmp[v] == -1) rdfs(v, k);}}ll scc(){for(ll i = 0; i < 2 * V; ++i){if(cmp[i] == -1) dfs(i);}fill(cmp.begin(), cmp.end(), -1);ll k = 0;for(ll i = 2 * V - 1; i >= 0; --i){if(cmp[ps[i]] == -1) rdfs(ps[i], k++);}return k;}public:vector<ll> ans;TwoSAT(const ll literal_count): V(literal_count), G(2 * V), rG(2 * V), cmp(2 * V, -1), ans(V){}void add_closure(ll x, ll y){add_edge((x + V) % (2 * V), y), add_edge((y + V) % (2 * V), x);}// 充足可能性判定// 真のものは 1,偽のものは 0 が ans に格納される(解の構成)bool satisfy(){scc();for(ll i = 0; i < V; i++){if(cmp[i] == cmp[V + i]) return false;ans[i] = (cmp[i] > cmp[V + i]);}return true;}};// --------- library end - //int main(){ll N, M;cin >> N >> M;// 使い方// ll n = リテラル数;// TwoSAT two_sat(n);// - ※a,¬a,b,¬b,c,¬c があるとき、n = 3 。頂点倍加はライブラリ内で行う。// for(各クロージャ) add_closure(x, y); // これは クロージャ (x∨y) の例// - 否定(¬xや¬y)を入れたい時は +n する。ex) (¬x∨y) -> (x+n, y);// if (two_sat.satisfy()) rep(i, n) cout << two_sat.ans[i] << endl;// else cout << "impossible" << endl;vector<pll> LRs(N * 2, pll());rep(i, N){ll l = il();ll r = il();LRs[i] = m_p(l, r);LRs[i+N] = m_p(M-1-r, M-1-l);}TwoSAT two_sat(N); // リテラル = ブロックrep(i, N) rep(j, i){// i: 0~N-1, j: 0~i-1 なので、実質 i: 1~N-1, j: 0~i-1// つまり、j < i な 各ブロックについてループ// i と j のピンク区間がかぶったとき、iとj, ¬iと¬jが両立しないので、(¬i∨¬j) ∧ (i∨j)// 区間がかぶる = !(iR < jL || jR < iL)if (!(LRs[i].second < LRs[j].first || LRs[j].second < LRs[i].first)){two_sat.add_closure(i+N, j+N);two_sat.add_closure(i, j);}// i と ¬j のピンク区間がかぶったとき、iと¬j, ¬iとjが両立しないので、(¬i∨j) ∧ (i∨¬j)if (!(LRs[i].second < LRs[j+N].first || LRs[j+N].second < LRs[i].first)){two_sat.add_closure(i+N, j);two_sat.add_closure(i, j+N);}}if (two_sat.satisfy()) cout << "YES" << endl;else cout << "NO" << endl;}