2015年2月27日金曜日

JOI春合宿2014 Day2-1 Water Bottle

解法

隣り合っている建物について辺を張って,最小全域木作って,Queryに早く答える。
BFSすれば,隣り合っている建物がわかって嬉しい(経路中に建物をまたぐ移動は嬉しくないのは自明)
これでO(HW)。
それで,辺のコストが小さい順につないでいけば,最短距離がわかって(集合と集合がつながったタイミングのコスト)さらに嬉しい。俗に言う最小全域木。
この際に,俗に言うデータ構造をマージする一般的なテクを使ったら部分点70が生えた。
よくよく考えると、O(HW + HW log HW + (P log^2 Q)) ≒ O(2*10^8)くらい(適当)。
間に合ったりしても良さそうだけど,定数や入出力が重かったりしてわりとつらい。(←嘘ついた、マージしても間に合う。(下のソース)
さらに高速化を考えなければいけないんだけど、そのときにLCAを使うとよい。
最大値もダブリングして一緒に計算すれば,1つのクエリーに対数時間で答えることが出来て、 O(HW+HWlogHW+(P + Q)logP) ≒ O(10^8)になって,100点が生える。

ソース

#include<bits/stdc++.h>
using namespace std;
struct edge {
  int u, v, cost;
  bool operator<(const edge& e) const {
    return cost < e.cost;
  }
  bool operator==(const edge& e) const {
    return(u == e.u && v == e.v && cost == e.cost);
  }
};
struct edge2 { 
  int to, cost;
};
typedef pair< int, int > Pi;
typedef pair< int, Pi > Pii;
typedef vector< edge > Edges;
typedef vector< vector< edge2 > > Graph;
const int INF = 1 << 30;
const int LOG_MAX = 20;

struct UnionFind {
  vector< int > data;
  UnionFind(int sz) {
    data.assign( sz, -1);
  }
  int Union(int a, int b) {
    a = Find(a), b = Find(b);
    if(data[a] < data[b]) swap( a, b);
    data[b] += data[a];
    data[a] = b;
    return(b);
  }
  int Find(int a) {
    if(data[a] < 0) return(a);
    else return(data[a] = Find(data[a]));
  }
};

int H, W, P, Q;
int A[200000], B[200000], S[200000], T[200000];
bool mas[2000][2000];
Pi Number[2000][2000];
Edges edges;
Graph graph;
int Depth[200000];
int up[LOG_MAX][200000];
int upc[LOG_MAX][200000];


void bfs() {
  queue< Pi > que;
  static const int dy[] = { 0, 1, 0, -1}, dx[] = { 1, 0, -1, 0};
  fill_n( *Number, 2000 * 2000, make_pair(-1, -1));
 
  for(int i = 0; i < P; i++) {
    que.push(make_pair(B[i], A[i]));
    Number[A[i]][B[i]] = make_pair(0, i);
  }
  while(!que.empty()){
    Pi p = que.front(); que.pop();
    for(int i = 0; i < 4; i++) {
      int nx = p.first + dx[i], ny = p.second + dy[i];
      if(nx < 0 || ny < 0 || nx >= W || ny >= H || mas[ny][nx]) continue;
      if(Number[ny][nx] != make_pair(-1, -1)) continue;
      Number[ny][nx] = Number[p.second][p.first];
      Number[ny][nx].first++;
      que.push(make_pair(nx, ny));
    }
  }
  return;
}
 
void make_graph() {
  for(int i = 0; i < H; i++) {
    for(int j = 0; j + 1 < W; j++) {
      int u = Number[i][j].second, v = Number[i][j + 1].second;
      if(u < 0 || v < 0 || u == v) continue;
      int cost = Number[i][j].first + Number[i][j + 1].first;
      edges.push_back((edge){u, v, cost});
    }
  }
  for(int i = 0; i + 1 < H; i++) {
    for(int j = 0; j < W; j++) {
      int u = Number[i][j].second, v = Number[i + 1][j].second;
      if(u < 0 || v < 0 || u == v) continue;
      int cost = Number[i][j].first + Number[i + 1][j].first;
      edges.push_back((edge){u, v, cost});
    }
  }
  return;
}

void make_MST() {
  graph.resize(P);
  UnionFind uf(P);

  sort( edges.begin(), edges.end());
  edges.erase(unique(edges.begin(), edges.end()), edges.end());
  for(int i = 0; i < edges.size(); i++){
    edge& e = edges[i];
    if(uf.Find(e.u) == uf.Find(e.v)) continue;
    uf.Union( e.u, e.v);
    graph[e.u].push_back((edge2){e.v, e.cost});
    graph[e.v].push_back((edge2){e.u, e.cost});
  }

  for(int i = 1; i < P; i++) {
    if(uf.Find(i - 1) == uf.Find(i)) continue;
    graph[i - 1].push_back((edge2){i, INF});
    graph[i].push_back((edge2){i - 1, INF});
    uf.Union(i - 1, i);
  }
  
}
int dfs(int idx, int prev, int cost) {
  if(prev == -1) {
    for(int i = 0; i < LOG_MAX; i++) {
      up[i][idx] = -1;
    }
    Depth[idx] = 0;
  } else {
    up[0][idx] = prev, upc[0][idx] = cost;
    Depth[idx] = Depth[prev] + 1;
    for(int i = 1; i < LOG_MAX; i++) {
      if(up[i - 1][idx] < 0) {
        up[i][idx] = -1;
      } else {
        up[i][idx] = up[i - 1][up[i - 1][idx]];
        upc[i][idx] = max( upc[i - 1][idx], upc[i - 1][up[i - 1][idx]]);
      }
    }
  }
  for(int i = 0; i < graph[idx].size(); i++) {
    edge2& e = graph[idx][i];
    if(e.to != prev) dfs( e.to, idx, e.cost);
  }
}

void make_LCA() {
  dfs( 0, -1, 0);
}

int queryLCA(int a, int b) {
  if(Depth[a] > Depth[b]) swap(a, b);

  int ret = 0;
  for(int k = LOG_MAX - 1; k >= 0; --k) {
    if((Depth[b] - Depth[a]) >> k & 1){
      ret = max(ret, upc[k][b]);
      b = up[k][b];
    }
  }
  if(a == b) return(ret);
  for(int k = LOG_MAX - 1; k >= 0; --k) {
    if(up[k][a] != up[k][b]) {
      ret = max( ret, max( upc[k][a], upc[k][b]));
      a = up[k][a]; b = up[k][b];
    }
  }
  return(max(ret, max(upc[0][a], upc[0][b])));
}

 
int main() {
 
  scanf("%d %d %d %d", &H, &W, &P, &Q);
  getchar();
  for(int i = 0; i < H; i++) {
    for(int j = 0; j < W; j++) {
      char c;
      scanf("%c", &c);
      mas[i][j] = (c == '#');
    }
    getchar();
  }
  for(int i = 0; i < P; i++) {
    scanf("%d %d", &A[i], &B[i]);
    --A[i], --B[i];
  }
  for(int i = 0; i < Q; i++) {
    scanf("%d %d", &S[i], &T[i]);
    --S[i], --T[i];
  }
 
  bfs();
  make_graph();
  make_MST();
  make_LCA();

  int ret;
  for(int i = 0; i < Q; i++){
    printf("%d\n", (ret = queryLCA(S[i], T[i])) < INF ? ret : -1);
  }
}
#include<bits/stdc++.h>
using namespace std;
struct edge {
  int u, v, cost;
  bool operator<(const edge& e) const {
    return cost < e.cost;
  }
  bool operator==(const edge& e) {
    return(u == e.u && v == e.v && cost == e.cost);
  }
};
typedef pair< int, int > Pi;
typedef pair< int, Pi > Pii;
typedef vector< edge > Edges;
const int INF = 1 << 30;
 
int H, W, P, Q;
int A[200000], B[200000], S[200000], T[200000];
bool mas[2000][2000];
Pi Number[2000][2000];
set< int > data[200000];
Edges edges;
int answer[200000];
 
struct UnionFind {
  vector< int > dat;
  UnionFind(int sz) {
    dat.assign( sz, -1);
  }
  int Union(int a, int b) {
    a = Find(a), b = Find(b);
    if(data[a].size() > data[b].size()) swap(a, b);
    dat[b] += dat[a];
    dat[a] = b;
    return(b);
  }
  int Find(int a) {
    if(dat[a] < 0) return(a);
    else return(dat[a] = Find(dat[a]));
  }
};
 
void bfs() {
  queue< Pi > que;
  static const int dy[] = { 0, 1, 0, -1}, dx[] = { 1, 0, -1, 0};
  fill_n( *Number, 2000 * 2000, make_pair(-1, -1));
 
  for(int i = 0; i < P; i++) {
    que.push(make_pair(B[i], A[i]));
    Number[A[i]][B[i]] = make_pair(0, i);
  }
  while(!que.empty()){
    Pi p = que.front(); que.pop();
    for(int i = 0; i < 4; i++) {
      int nx = p.first + dx[i], ny = p.second + dy[i];
      if(nx < 0 || ny < 0 || nx >= W || ny >= H || mas[ny][nx]) continue;
      if(Number[ny][nx] != make_pair(-1, -1)) continue;
      Number[ny][nx] = Number[p.second][p.first];
      Number[ny][nx].first++;
      que.push(make_pair(nx, ny));
    }
  }
  return;
}
 
void make_graph() {
  for(int i = 0; i < H; i++) {
    for(int j = 0; j + 1 < W; j++) {
      int u = Number[i][j].second, v = Number[i][j + 1].second;
      if(u < 0 || v < 0 || u == v) continue;
      int cost = Number[i][j].first + Number[i][j + 1].first;
      edges.push_back((edge){u, v, cost});
    }
  }
  for(int i = 0; i + 1 < H; i++) {
    for(int j = 0; j < W; j++) {
      int u = Number[i][j].second, v = Number[i + 1][j].second;
      if(u < 0 || v < 0 || u == v) continue;
      int cost = Number[i][j].first + Number[i + 1][j].first;
      edges.push_back((edge){u, v, cost});
    }
  }
  return;
}
 
int main() {
 
  scanf("%d %d %d %d", &H, &W, &P, &Q);
  getchar();
  for(int i = 0; i < H; i++) {
    for(int j = 0; j < W; j++) {
      char c;
      scanf("%c", &c);
      mas[i][j] = (c == '#');
    }
    getchar();
  }
  for(int i = 0; i < P; i++) {
    scanf("%d %d", &A[i], &B[i]);
    --A[i], --B[i];
  }
  for(int i = 0; i < Q; i++) {
    scanf("%d %d", &S[i], &T[i]);
    --S[i], --T[i];
    data[S[i]].insert(i);
    data[T[i]].insert(i);
  }
 
  bfs();
  make_graph();
  sort( edges.begin(), edges.end());
  edges.erase(unique(edges.begin(), edges.end()), edges.end());
  
  UnionFind uf(P);
  fill_n( answer, Q, -1);
  
  for(int i = 0; i < edges.size(); i++){
    edge& e = edges[i];
    if((e.u = uf.Find(e.u)) == (e.v = uf.Find(e.v))) continue;
    int unioned = uf.Union( e.u, e.v);
    int u = e.u, v = e.v;
    if(unioned == u) swap(u, v);
    for(set< int >::iterator it = data[u].begin(); it != data[u].end(); ++it){
      set< int >::iterator cell = data[v].find(*it);
      if(cell != data[v].end()){ // あったらそのときのコストが解
        answer[*it] = e.cost;
      } else {
        data[v].insert(*it);
      }
    }
  }
  for(int i = 0; i < Q; i++){
    printf("%d\n", answer[i]);
  }
}

2015年2月8日日曜日

JOI2015本選参加記

眠れない日々だった
3完 + 部分点 の 312 点でした
本選通過したようです
プロがたくさん生える合宿だ><つらい

  • 自分の高校からは4人出場らしいので、部活の顧問を加えて5人でオリセンに入ろうとする
  • 怖い
  • まだ時間がありそう
  • なので、その前に明治神宮へ行き神頼みを実施する
  • 荷物が重い
  • 15:00くらいにチェックイン
  • 大会のプレッシャーに押し潰されそうになる
  • 落ち潰された
  • プラクティスフェーズが生える
  • 自分の席の位置が一番前の真ん中でハラスメント
  • 緊張する
  • やろうとする
  • Firefoxが固まってる(訴訟
  • 自分のせいだつらい(悲観
  • スタッフの方々が対処してくれたみたい
  • 説明を何も聞いてなかった
  • 察する
  • ソースコードが保存できない
  • 自分が押してたのは Ctrl+X,Sだと思っていたのに Fn+X,Sだった
  • キーボードの配置がつらくて首元が冷える
  • 30分くらいで全完する
  • 暇だったので1問目に悩む
  • 遅延評価のセグメント木とBinaryIndexTreeで解けそうだったので実装する
  • できてるみたい
  • 講演会が生える
  • 聞く
  • 機械学習やってみたいね
  • 食べる
  • お腹すいてたのでたくさん食べる
  • コーラをこぼしそうになる
  • 自己紹介フェーズがあるみたい
  • ハラスメント的な自己紹介があったりと多種多様
  • 自分は何言おうかコミュ障並の頭で悩む
  • 緊張する
  • 1人あたりの時間が少なそうだから,言いたいことを1つに決める
  • 『男の子が好きです、その気がある人は(ry』
  • 最後の方に付随者が生えて嬉しい
  • その彼から名刺を貰う
  • 嬉しい
  • その後宿泊施設へ
  • 全員個室らしい
  • 同校の人たちと色々話す
  • 11時頃に睡眠フェーズがやってきた
  • 寝る
  • 寝れない
  • 寝たふりをする
  • 寝れない
  • 起きる
  • 寝れない
  • 緊張(別の理由があるきがする)のあまり眠れない
  • 気づいた時には2時半
  • つらいことを考え始める
  • JOI本選0完不可避
  • さすがに焦りを感じ寝る努力をする
  • 寝れない
  • 寝る→寝れない→寝る→・・・の閉路ができてた気がする
  • これSCCしてみたら1つしか強連結成分が無いんだなあとよくわかんないことを考えはじめる
  • 気づいた時には5時半
  • つらい
  • 頭が重い
  • 自分のような人は睡眠フェーズに失敗するのは自明だったんだと後悔する
  • この時間に本選落ちが確定してる
  • かなりねむい
  • とてもねむい
  • 朝食
  • ねむくてよくわかんないや
  • ふつうに美味しかったと思う
  • 待機室へ
  • 気持ち悪いつらい
  • 絶望
  • 競技会場へ
  • ここまで来たら仕方ない
  • やるしかNASA
  • OverViewシートから察しようとする
  • 2問目のメモリ制約と制限時間が目立つ
  • 始まった
  • 1問目
  • むずい
  • なんか難化している
  • 軟化な気がする
  • 眠くてよくわからない
  • ちょっと考えると、その鉄道の使用回数がわかればあとは比べるだけということがわかる
  • セグメントツリーの範囲加算が使えそう(オーバーKILL)
  • 流石に眠い頭でこんなものを実装できるわけない&これは1問目だよなと疑う
  • imos法が生える
  • Imos[i] += Imos[i - 1];だけでいいのに焦りしかないのでよくわかんないことをする
  • バグらせる
  • 30分が経過しそう
  • 焦る
  • 経過した
  • 人権を失う
  • 多分みんな1問目は既に通してるんだよなあと自分の頭の無さに絶望する
  • よくわからないデバッグをしてサンプル一致したので提出
  • Accept
  • 家帰って書いたソースコードです(落ち着いてかけてるからソースは汚くない)
  • #include<bits/stdc++.h>
    using namespace std;
    typedef long long int64;
    int main(){
      int N, M;
      cin >> N >> M;
      vector< int > P(M);
      for(int i = 0; i < M; i++){
        cin >> P[i];
        --P[i];
      }
      vector< int > A(N - 1), B(N - 1), C(N - 1);
      for(int i = 0; i < N - 1; i++){
        cin >> A[i] >> B[i] >> C[i];
      }
      vector< int64 > Imos( N + 1, 0);
      for(int i = 1; i < M; i++){
        int u = P[i - 1], v = P[i];
        if(u > v) swap( u, v);
        ++Imos[u];
        --Imos[v];
      }
      for(int i = 1; i < N - 1; i++){
        Imos[i] += Imos[i - 1];
      }
    
      int64 ret = 0;
      for(int i = 0; i < N - 1; i++){
        ret += min( A[i] * Imos[i], C[i] + B[i] * Imos[i]);
      }
      cout << ret << endl;
    }
    
  • レスポンスがFastって感じだ
  • 提出後フィードバックは15分以内はとても保健があると思う
  • 2問目
  • 焦ってるのでよくわからない
  • どう考えてもDPっぽい
  • DP書こうとする
  • O(N^3)しか無理じゃんと思ってやめる
  • どうするんだろう(途方
  • 難しそうだしやめよう(これがダメだった
  • 人権を失った(2回目)
  • 3問目を見る
  • さらに焦ってるのでさらによくわからない
  • ダイクストラして二分探索して最小全域木作って累積和だとかいう嘘々ワードが計算用紙に飛び交う
  • つらい
  • 予選落ちするべきだったんだと後悔する
  • とりあえず話にならないから、やるだけを書く
  • できない
  • そもそもDijkstraをバグらせてる
  • うれしい(うれしくない
  • Dijkstraを世界一書けない
  • プラクティスでDijkstraを実装するべきだった
  • いろいろ頭悪そうなことしてる
  • Dijkstraできる
  • 制約的にやるだけでも部分点60をとれそう
  • RE(部分点60)
  • 読み通り
  • ここらへんで2時間が経過した気がする
  • 目が覚めてくる
  • 高速化は意外にも部分点ソースを眺めていると自明にできそう
  • 単調増加になっている
  • 前のコスト的なのを保存してやるだけ
  • Accept
  • 嬉しい
  • 水500ml一本を消費
  • トイレへ行きたくなる
  • 行く
  • すっきり
  • 2問目に戻る
  • 目が覚めてきたのでさっきまで頭数が開始から単調減少して負数個になっていたけどここで増加し始める
  • メモリ制約から察するに dp[2000][2000][2]を確信
  • [2]はいらなかったらしいけど定数倍な
  • ループむずい
  • 既出問でこんなものがあった気がする
  • 具体的には ICPC Summer 2011 - Magical Girl Sayaka-chan これを思い出す
  • 円環を2つの直線で考えたような気がする
  • よくよく考えれば dp配列は開始位置が変わってもそのままでよかったことに気づく(遅い
  • 書く
  • すらすら書ける
  • バグる
  • 人権を失う(3回目
  • ダメだ
  • この辺で3時間が経過した気がする
  • 焦る
  • 最後のピースを無視してただけだった
  • #include<bits/stdc++.h>
    using namespace std;
    typedef long long int64;
    
    int N;
    vector< int > A;
    int64 dp[2000][2000];
    inline int NEXT(int a){ return((a + 1) % N); }
    inline int PREV(int a){ return((a + N - 1) % N); }
    
    inline int64 rec(int left, int right, bool flag) {
      if(left == right) return(flag ? 0 : A[left]);
      if(~dp[left][right]) return(dp[left][right]);
      int64 ret = 0;
      if(flag) {
        if(A[left] < A[right]) ret = rec( left, NEXT(right), false);
        else ret = rec(PREV(left), right, false);
      } else {
        ret = max( rec(PREV(left), right, true) + A[left], rec(left, NEXT(right), true) + A[right]);
      }
      return(dp[left][right] = ret);
    }
    
    
    int main()
    {
      fill_n( *dp, 2000 * 2000, -1);
    
      cin >> N;
      A.resize(N);
      for(int i = 0; i < N; i++) {
        cin >> A[i];
      }
      int64 ret = 0;
      for(int i = 0; i < N; i++) {
        ret = max( ret, rec( PREV(i), NEXT(i), true) + A[i]);
      }
      cout << ret << endl;
    
      return(0);
    }
    
  • これも後日書いたやつ。余分な次元を省略してあります
  • 提出
  • Accept
  • 大分落ち着きを取り戻す
  • だけどノーマル3完はプロN人(N≧20)に負けると思う
  • 4問目か5問目
  • 4問目を読む
  • やりたいことはすぐわかる
  • これをどうやって効率的に解くのか全くとして分からない
  • 考える
  • わからない
  • 時間が終了に近づいてきていることに気づく
  • もうちょっと考える
  • わからない(こなみ
  • THE 全探索
  • これ以降は少ない時間の中,全探索に特化する人間になることを決意する
  • USE next_permutation()
  • WA 0点
  • つらすぎでしょ
  • 全探索すら書けない
  • 人権を失う(INF回目
  • 特に間違いが見当たらないので再度提出(意味がない
  • WA 0点(2回目
  • 本当に間違いが見当たらないので再々度提出(意味がさらにない
  • WA 0点(3回目
  • なんか全探索できてなさそうなので適当にprintf()デバッグする
  • なぜかnext_permutation()で全パターンが生成されていない(おどろいた(こなみ
  • 予選のときもあったような気がしたけどGCCからのハラスメントに違いないと思う
  • よくわからなさ
  • THE 再帰
  • next_permutation()が使えないなら再帰を書いて全列挙
  • 学校の授業が役に立ってバグをはやさなかった
  • 提出
  • 部分点 8点
  • うれしい(うれしくない
  • GCCの使用をやめたくなる
  • こんな部分点誰でも取れると自戒して5問目の部分点を狙う
  • 2次元累積和
  • 忘れた
  • 5問目の部分点4点すら取れない
  • つらいので帰りたくなる
  • よくよく考えれば何やっても間に合いそう
  • 1次元の累積和
  • 書けた
  • たくさんループを回す
  • 提出
  • 部分点 4点
  • ここから20分くらいある
  • 4問目が全くわからないのでまだ5問目が可能性あるなあと思って考察
  • 障害物が小さいケースは全通りの枠から引っかかる枠を引けば早そうだと気づく(残り15分
  • 算数できない
  • 全通りの計算はかろうじて出来た
  • あとはごにょごにょやろうとする
  • 重複があることに気づく(残り10分
  • どうやって処理するんだろう
  • 終わり
  • 解説聞く
  • 得点分布で身の危険を感じる
  • ボーダー 304, 308, 312, 324あたりっぽい
結論:ここまで寝れないとは思わなかった

4問目を解いた。二分探索してDPっぽい。
解説聞けばなるほどって感じだけどわかんなかった。
#include<bits/stdc++.h>
using namespace std;
typedef long long int64;
const int64 INF = 1LL << 55;

int toChild[1000000][3];
int Buffer[99999];
vector< int > Dwill;
int64 dp[1000000];
int N, M;

int MakeTree(){
  queue< int > que;
  for(int i = 0; i < N; i++){
    que.push(i);
    toChild[i][0] = -1;
    toChild[i][1] = -1;
    toChild[i][2] = -1;
  }
  int nextIndex = N;
  while(que.size() > 1){
    int a = que.front(); que.pop();
    int b = que.front(); que.pop();
    int c = que.front(); que.pop();

    toChild[nextIndex][0] = a;
    toChild[nextIndex][1] = b;
    toChild[nextIndex][2] = c;
    que.push(nextIndex);
    ++nextIndex;
  }
  return(que.front());
}

int64 getDP(const int64& Value, int index) {
  if(~dp[index]) return(dp[index]);
  if(toChild[index][0] == -1){
    if(Buffer[index] == 0) return(1);
    else if(Buffer[index] >= Value) return(0);
    else return(INF);
  } else {
    int64 array[] = { getDP(Value, toChild[index][0]), getDP(Value, toChild[index][1]), getDP(Value, toChild[index][2])};
    sort( array, array + 3);                                                                                             
    return(dp[index] = min(INF - 1, array[0] + array[1]));
  }
}

bool Calclation(int64 Value, const int& root){
  int High = Dwill.end() - lower_bound( Dwill.begin(), Dwill.end(), Value);
  fill_n( dp, 1000000, -1);
  return(getDP(Value, root) <= High);
}

int main() {
  cin >> N >> M;
  for(int i = 0; i < M; i++){
    int D, P;
    cin >> D >> P;
    --P;
    Buffer[P] = D;
  }
  Dwill.resize(N - M);
  for(int i = 0; i < N - M; i++){
    cin >> Dwill[i];
  }
  sort( Dwill.begin(), Dwill.end());
  int root = MakeTree();
  int64 low = 0, high = INF;
  while(high - low > 0){
    int64 mid = (low + high + 1) / 2;
    if(Calclation(mid, root)) low = mid;
    else high = mid - 1;
  }
  cout << low << endl;
}

2015年2月4日水曜日

JOI春合宿2008 Day4 Typhoon

解法

適当に座標圧縮してセグ木に台風の情報を持たせれば通る問題。
持たせ方に迷ったけど、そのまま節に突っ込めばO(Qlog(N+Q)logK)くらいになっていい感じになることに気づいた。
台風が小さい方から追加していけば節の台風の番号群は昇順になるから、それからクエリごとにセグメントツリー的に範囲を二分していって、二分探索してサイズを求めれば良さがある。

ソース

#include<bits/stdc++.h>
using namespace std;
typedef long long int64;
typedef pair< int, int > Pi;
typedef pair< int, Pi > Pii;
 
vector< int > nums;
int n, m, k, a[100000], b[100000];
int p[100000], q[100000], r[100000];
 
const int sz = 1 << 19;
vector< int > seg[2 * sz + 1]; 
 
void add(int a, int b, int value, int k, int l, int r){
  if(a >= r || b <= l) return;
  if(a <= l && r <= b) {
    seg[k].push_back(value);
  } else {
    add( a, b, value, 2 * k + 1, l, (l + r) >> 1);
    add( a, b, value, 2 * k + 2, (l + r) >> 1, r);
  }
}
int query(int a, int b, int tA, int tB, int k, int l, int r){
  if(a >= r || b <= l) return(0);
  if(a <= l && r <= b) return(lower_bound( seg[k].begin(), seg[k].end(), tB) - lower_bound( seg[k].begin(), seg[k].end(), tA));
  return query( a, b, tA, tB, 2 * k + 1, l, (l + r) >> 1) + query( a, b, tA, tB, 2 * k + 2, (l + r) >> 1, r) + lower_bound(seg[k].begin(), seg[k].end(), tB) - lower_bound( seg[k].begin(), seg[k].end(), tA);
}
 
 
int main(){
  cin >> n >> m >> k;
  for(int i = 0; i < n; i++){
    cin >> a[i] >> b[i];
    nums.push_back(a[i]);
    nums.push_back(b[i]);
  }
  for(int i = 0; i < m; i++){
    cin >> p[i] >> q[i] >> r[i];
    nums.push_back(p[i]);
  }
  sort( nums.begin(), nums.end());
  nums.erase( unique(nums.begin(), nums.end()), nums.end());
  for(int i = 0; i < n; i++){
    a[i] = lower_bound( nums.begin(), nums.end(), a[i]) - nums.begin();
    b[i] = lower_bound( nums.begin(), nums.end(), b[i]) - nums.begin();
  }
  for(int i = 0; i < m; i++){
    p[i] = lower_bound( nums.begin(), nums.end(), p[i]) - nums.begin();
  }
  for(int i = 0; i < n; i++){
    add( a[i], b[i] + 1, i + 1, 0, 0, 1 << 19);
  }
  for(int i = 0; i < m; i++){
    cout << query( p[i], p[i] + 1, q[i], r[i] + 1, 0, 0, 1 << 19) << endl;
  }
}