/* Rozwiazanie zadania Ryby z IOI 2008 * Autor: Filip Wolski */ #include #include #include #include #define MXN 500000 using namespace std; struct fish_t { int len, gem; }; int n, m, mod; fish_t fish[MXN]; int last[MXN], all[MXN], milestone[MXN], colorsfirstcount[MXN], vis[MXN]; pair setofz[MXN]; bool cmp(const fish_t &o, const fish_t &p) { return o.len < p.len; } class IntervalTree { public: struct Node { Node *left, *right, *father; int value; }; private: int size; Node* root; Node** tab; int Get(Node* w, int wmin, int wmax, int min, int max) { if (wmax < min) return 1; if (wmin > max) return 1; if (min <= wmin && wmax <= max) return w->value; int middle = (wmin + wmax) / 2; return (Get(w->left, wmin, middle, min, max) * Get(w->right, middle+1, wmax, min, max)) % mod; } void Init(Node* &w, int min, int max) { w = new Node; w->value = 1; if (min == max) { tab[min] = w; w->left = NULL; w->right = NULL; return; } int middle = (min + max) / 2; Init(w->left, min, middle); w->left->father = w; Init(w->right, middle+1, max); w->right->father = w; } void CleanUp(Node* w) { if (w == NULL) return; CleanUp(w->left); CleanUp(w->right); delete w; } public: IntervalTree(int size) : size(size), tab(new Node*[size]) { Init(root, 0, size-1); root->father = NULL; } ~IntervalTree() { CleanUp(root); delete[] tab; } void Change(int n, int new_value) { Node *pos = tab[n]; pos->value = new_value; while ((pos = pos->father) != NULL) { pos->value = (pos->left->value * pos->right->value) % mod; } } int Get(int min, int max) { return Get(root, 0, size-1, min, max); } }; IntervalTree *A, *B; int main() { scanf("%d%d%d", &n, &m, &mod); for (int a = 0; a < n; a++ ) { scanf("%d%d", &fish[a].len, &fish[a].gem); fish[a].gem--; } B = new IntervalTree(m); fill(last, last+m, -1); fill(all, all+m, 0); fill(vis, vis+m, -1); sort(fish, fish+n, cmp); for (int a = 0; a < n; a++ ) { last[fish[a].gem] = a; all[fish[a].gem]++; } for (int a = 0; a < m; a++ ) { B->Change(a, all[a]+1); } fill(milestone, milestone+m, -1); for (int a = 0; a < n; a++ ) { if (fish[a].len * 2 > fish[last[fish[a].gem]].len && milestone[fish[a].gem] == -1) { milestone[fish[a].gem] = a; } } int pos = n; int firstcount = n; int numberofz = 0; fill(colorsfirstcount, colorsfirstcount + m, -1); while (pos--) { while (firstcount > 0 && fish[firstcount-1].len * 2 > fish[pos].len) firstcount--; if (colorsfirstcount[fish[pos].gem] == -1) { colorsfirstcount[fish[pos].gem] = firstcount; setofz[numberofz++] = make_pair(firstcount, fish[pos].gem); } } sort(setofz, setofz + numberofz); A = new IntervalTree(numberofz); pos = n; firstcount = n; int res = 0; while (pos--) { if (vis[fish[pos].gem] == -1) { int gem = fish[pos].gem; while (firstcount > colorsfirstcount[gem]) { int gem = fish[--firstcount].gem; if (vis[gem] == -1) { all[gem]--; B->Change(gem, all[gem]+1); } else { all[gem]--; A->Change(vis[gem], all[gem]+1); } } int milestone_scaled = upper_bound(setofz, setofz + numberofz, make_pair(milestone[gem], m)) - setofz; int firstcount_scaled = lower_bound(setofz, setofz + numberofz, make_pair(firstcount, gem)) - setofz; int prod_max = milestone_scaled == 0 ? 1 : A->Get(0, milestone_scaled-1); A->Change(firstcount_scaled, all[gem]+1); B->Change(gem, 1); int prod = B->Get(0, m-1); prod_max = (prod_max * prod) % mod; prod = (prod * all[gem]) % mod; res = (res + prod + prod_max) % mod; vis[gem] = firstcount_scaled; } } printf("%d\n", res); delete A; delete B; return 0; }