【NOI 2015]プログラムは自動的に、離散処理と互いに素なセット解析します

タイトル説明

自動分析のプログラムの実施中には、制約の数を同時に満たすことができるかどうかを決定することがしばしば必要です。

制約充足問題の簡略化を考慮してくださいと仮定X1、X2、X3、...に現れるプログラム変数を表し、フォームXI = XJのXI≠XJ変数又は等しい/等しくない制約基準のN与えられ、それはかどうかが判定されます上記のすべての制約を同時に満たすようにこれは、各変数に適切な値のために別々に与えられてもよいです。例えば、制約の問題がある:X1 = X2、X2 = X3 、X3 = X4、×1≠×4、 これらの制約は明らかに同時に満たすことができないので、この問題は、満足することを決定することはできません。
今、彼らが判断されるようにして、いくつかの制約充足問題を与えます。

エントリー

入力ファイルは、それが問題の数を決定することを示し、最初の行の正の整数t含みます。これらの間で個別の問題であることに注意してください。

複数のラインを構成する各質問について:
1行目は、正の整数nが含まれている、条件を発行する必要がある制約の数が満たされています。
続いてN行、三つの整数I、J、Eを含む各行は、隣接する整数の間の単一のスペースで区切られた等しい/等しくない制約を記載しました。E = 1の場合、制約は= XJ XIであり、e = 0の場合、制約はXI≠XJあります。

輸出

トン行を含む出力ファイル。

出力ファイル出力文字列「YES」または「NO」(引用符は、すべて大文字なし)、「YES」k番目の入力は問題を満足できると判断された意味のK行目は、「NO」ならない示し会います。

テスト入力

2
2
1 2 1
1 2 0
2
1 2 1
2 1 1

テスト出力

NO
YES

プロンプト

第一の問題では、制約がある:X1 = X2、X1≠X2。これらの2つの制約は、互いに矛盾するため、同時に満たすことはできません。

第二の問題では、制約がある:X1 = X2、X2 = X1。これらの2つの制約が等価である、それは同時に満足させることができます。

1≤n≤1000000

1≤i、j≤1000000000

トピック分析

関係の2種類の間の唯一の類似点と相違点は、その後、私たちはばらばらのコレクションを維持するためにばらばらのセットの同じ数の同じ関係のすべてに対処するために始めることができますので、この質問に関しては、目的は、プログラムは入力のすべてを満たしているかどうかを決定することですあるいは互いに素なセットは、その後、異なる数の、矛盾が同じセットで起こる異なる数の際、すべての異なる番号を処理し、NOの出力は、出力の異なるセットに属するようにそれを見つけるために横断することができるYES、本タイトル入力の数が大きいほど、離散処理の必要性は、アレイに直接開いていないことに注意してください

注意点

  1. 圧縮パス、オブジェクトの前に設定されたパスの圧縮は他のセットとマージすることができるとき、圧縮パスを使用する必要があるによる大量のデータをセット、この問題の確立を確認、注釈は、使用コードの一部ではありません従って、クエリ部分の消費を低減するだけ1木の木の深さ、(ときに大量のデータより明らか)
int find(int x){
    if(p[x] == x) return x;
    else{
        p[x] = find(p[x]);
    }
    return p[x];    
//  while(p[x] != x){
//      x = p[x];
//  }
//  return x;
}
  1. 約C ++ STLユニークである()関数で、その役割は、それが引用符と合併し、なぜ、隣接する同じ数の「マージ」を達成するためにソートされた配列を、完了するのでしょうか?アレイは、典型的には、配列、二つの引数をとる場合、実際には、アレイの合計の長さが変化しない、それは要素(具体的に説明されていない)を繰り返すフロントカバー部材の裏面をマージする連続プロセスである、ユニークな動作を行います先頭アドレスと終了アドレス、およびその戻り値は+1に、最後のチェックビットを再順序付け基準が完了した後配列、すなわち、配列1,1,2,2,4,5,5,6ユニーク(を通じて言うことですアレイは最初の5を注文、及びINT T =ユニーク(A + 8)堆積されるとなり、その後、+ 8)1,2,4,5,6,5,5,6 [5]参照され、いくつかは、具体的に考えられるので、もし通常、我々は、INT、T =ユニーク(A + 8)を書き込む - 、それが自然に5 T、次のコードであります
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;

int main(){
    int a[] = {1, 1, 2, 2, 4, 5, 5, 6};
    int t = unique(a, a + 8) - a;
    cout<<t<<endl;
    cout<<a[t]<<endl;
    for(int i = 0; i <= 7; i++){
        cout<<a[i]<<endl;
    }
    return 0;
}
  1. バイナリサーチの参照の番号の最初の発生の配列効率的な方法であるLOWER_BOUND()関数の使用に、それは3つのパラメータを受け付け、配列の最初のアドレスは、アレイの終了アドレスは、値の数を照会するために、一般的に、私たちは、私が最初の発生を見つけるために、数ある配列の添字位置に位置を取得し、その後、配列名を減算した結果をチェックアウトします
 i = lower_bound(a + 1, a + n + 1, number) - a;
  1. 離散:データの量なのでI、J素晴らしいが、入力の一つ万足まで、明らかに最大200万異なる番号があり、私たちは、この入力のすべてを保存するために200万大きな配列を構築することができますI、J(彼ら自身がかもしれないがしかし、ずっとこのアレイ上の200万人以上の実際に問題ありません)

タイトルコード

#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;

const int N = 1000005;
int p[N<<1];
int m[N<<1];
int x[N];
int y[N];
int type[N];
int n, cnt, num;
struct Node{            //结构体是用于存放那些不同的数对的i j的第一次在数组中出现的位置 
    int x, y;
}k[N<<1];

void merge(){           //合并重复元素,实际上是覆盖操作,数组长度未发生变化,temp存放最后一个有序位置的索引(因为-m-1) 
    sort(m + 1, m + num + 1);
    int temp = unique(m + 1, m + num + 1) - m - 1;
    for(int i = 1; i <= n; i++){
        x[i] = lower_bound(m + 1, m + temp + 1, x[i]) - m;  //lowerbound()前两个参数的闭区间和开区间 
        y[i] = lower_bound(m + 1, m + temp + 1, y[i]) - m;  //执行完成后x[i]存放m数组中第一次出现x[i]的位置 
    }
}

int find(int x){
    if(p[x] == x) return x;
    else{
        p[x] = find(p[x]);
    }
    return p[x];    
//  while(p[x] != x){
//      x = p[x];
//  }
//  return x;
}

void Union(int x, int y){
    int fx = find(x);
    int fy = find(y);
    if(fx != fy) p[fy] = fx;
}

int main(){
    int t;
    scanf("%d", &t);
    while(t--){
        scanf("%d", &n);
        cnt = 0;
        num = 0;
        for(int i = 1; i <= n; i++){                    //对输入的n组输入进行2*n的离散化处理 
            scanf("%d%d%d", &x[i], &y[i], &type[i]);
            m[++num] = x[i];
            m[++num] = y[i];
        }
        for(int i = 1; i <= num; i++) p[i] = i;         //p数组存放根 
        merge();
        for(int i = 1; i <= n; i++){
            if(type[i] == 1){
                Union(x[i], y[i]);              //type == 1时 将根合并同时路径压缩,这里要注意,x[i] y[i] 
            }else{                              //存放的是第一次出现位置,只有这样才能在2000000空间中查询1000000000大的数 
                k[++cnt].x = x[i];              //k结构体数组存放type == 0时数对x[i],y[i]第一次在数组m中出现的位置 
                k[cnt].y = y[i];                
            }
        }
        int flag = 0;
        for(int i = 1; i <= cnt; i++){
            int fx = find(k[i].x);
            int fy = find(k[i].y);
            if(fx == fy){                       //如果根相同 则说明相等 矛盾 
                flag = 1;
                printf("NO\n");
                break;
            }
        }
        if(flag == 0){
            printf("YES\n");
        }
    }
    return 0;
}

おすすめ

転載: www.cnblogs.com/findview/p/11294833.html