#include #include #define MOD 1000000007 #define MAX 100000 #define LOGMAX 17 struct rng { int a, b; }; // N počet přihrádek na rostliny, _M počet zadaných intervalů, // M je počet intervalů po jejich setřídění a vyhození přebutečných int N, _M, M; struct rng _I[MAX], I[MAX]; long long D[MAX]; long long mocniny_2[LOGMAX + 1]; // V logaritmickém čase spočteme příslušnou mocninu dvou long long mocnina_2(int exponent) { long long vysledek = 1; for (int e = 0, m = 1; m <= exponent; e++, m *= 2) if (exponent & m) vysledek *= mocniny_2[e], vysledek %= MOD; return vysledek; } // Spočteme počet možností pro jedno schodiště začínající intervalem // I[odkud] mající l intervalů long long schodiste(int odkud, int l) { struct rng * J = I + odkud; D[0] = mocnina_2(J[0].b - J[0].a) - 1; // suma udávát součet hodnot (2^(J[k].b - J[k-1].b) - 1) * D[k-1] int p = 0; long long suma = 0; for (int i = 1; i < l; i++) { suma += (mocnina_2(J[i].b - J[i-1].b) - 1) * D[i-1]; suma %= MOD; while (J[p].b <= J[i].a) { long long o = (mocnina_2(J[p+1].b - J[p].b) - 1) * D[p]; // Nesmíme zapomenout, že modulení záporných čísle nemá přesně definované // chování, tedy se mu musíme vyhnout suma += (MOD - o % MOD), suma %= MOD; p++; } D[i] = suma, D[i] %= MOD; // Ještě interval [J[i].a, J[p].b] if (p == 0) { D[i] += (mocnina_2(J[0].b - J[i].a) - 1) * mocnina_2(J[i].a - J[0].a); D[i] %= MOD; } else { D[i] += ( (mocnina_2(J[p].b - J[i].a) - 1) * mocnina_2(J[p-1].b - J[i].a) ) % MOD * D[p-1]; D[i] %= MOD; } } return D[l-1]; } // Pomocná funkce pro quicksort void prohod(int x, int y) { struct rng t = _I[x]; _I[x] = _I[y]; _I[y] = t; } // Porovnávání pro quicksort // Proč zrovna toto uspořádání je vysvětleno u zpracování // setříděných intervalů bool mensi(int x, int y) { if (_I[x].a != _I[y].a) return _I[x].a < _I[y].a; return _I[x].b > _I[y].b; } // Setřiď prvky _I[l], ..., _I[r-1] void quicksort(int l, int r) { if (l < r) { // Za pivota vezmeme prostřední prvek a schováme si jej na začátek prohod(l, (l+r) / 2); // Hranice nám ukazuje, kam můžeme umístit prvek menší než pivot int hranice = l+1; for (int i = l+1; i < r; i++) if (mensi(i, l)) prohod(i, hranice++); // Umístíme správně pivota a spustíme rekurzi prohod(l, hranice - 1); quicksort(l, hranice - 1); quicksort(hranice, r); } } int main () { scanf("%d%d", &N, &_M); for (int i = 0; i < _M; i++) scanf("%d%d", &_I[i].a, &_I[i].b); // Po načtení vstupu potřebujeme intervaly uspořádat a // vyházet intervaly, které v sobě obsahují menší interval quicksort(0, _M); // Intervaly jsme si setřídili podle levého konce, v případě shody jsou nejdřív // intervaly s větším pravým koncem. Toto uspořádání způsobuje, že pokud má interval // v sobě celý jiný interval, je alespoň jeden takový interval jeho bezprostředním // následníkem. Díky tomu už jej snadno smažeme. for (int i = 0; i < _M; i++) { while (M && I[M-1].b >= _I[i].b) M--; I[M++] = _I[i]; } // Předpočítáme si mocniny dvojky mocnin dvojky pro následné rychlé mocnění mocniny_2[0] = 2; // 2^(2^0) for (int e = 1, m = 2; m <= N; e++, m *= 2) mocniny_2[e] = (mocniny_2[e-1] * mocniny_2[e-1]) % MOD; // 2^(2^e) = 2^(2^(e-1)) * 2^(2^(e-1)) int policka_ve_schodistich = 0; long long vysledek = 1; int s_zacatek = 0; // začátek schodiště for (int i = 0; i <= M; i++) { if (i != 0 && (i == M || I[i].a > I[i-1].b)) { // Skončilo aktuální schodiště I[s_zacatek], ..., I[i-1] vysledek *= schodiste(s_zacatek, i - s_zacatek); vysledek %= MOD; policka_ve_schodistich += I[i-1].b - I[s_zacatek].a; s_zacatek = i; } } // Zohledníme ještě obsazení políček, která se nenacházejí v žadném schodišti vysledek *= mocnina_2(N - policka_ve_schodistich); vysledek %= MOD; printf("Pocet moznosti (mod %d): %lld\n", MOD, vysledek); return 0; }