Паша та перестановки#12294

Паша та перестановки (Розбір)

Автор задачі:

Тунчай Пашаєв

Підготовлено:

Хасан Салех (MathModel)

Редагував:

Хасан Салех (MathModel)

Розв'язок

У нас є масив розміру , масив розміру та ціле число . Перестановка масиву називається красивою, якщо для кожного 1 \le i < m ми маємо b_{p_i} + b_{p_{i+1}} \equiv 0 \pmod{k}. У реалізації нижче ми використовуємо лише той факт, що позицій формують ланцюг довжини ; нам потрібен лише , а не фактичні індекси .

Ми попередньо обчислюємо факторіали та обернені факторіали за модулем 10^9+7 до N = 2\cdot 10^5, і визначаємо

P(n,r) = n P r = \frac{n!}{(n-r)!} \pmod{\text{MOD}}

як кількість способів вибрати та впорядкувати елементів з різних.

Для кожного тесту ми читаємо та будуємо:

  • : кратність точного значення в (для обробки дублікатів),

  • : кількість елементів з залишком r = x \bmod k.

Потім ми читаємо та ігноруємо його вміст, ми лише зберігаємо .

Якщо m = 1, то жодних обмежень немає, тому кожна перестановка мультимножини є красивою. Кількість різних перестановок дорівнює

\frac{n!}{\prod_x (\text{cnt}[x]!)} \pmod{\text{MOD}},

реалізовано як fact[n] \cdot \prod_x invfact[\text{cnt}[x]].

Тепер припустимо, що m \ge 2. Лише "спеціальні" позиції обмежені. Якщо якась позиція в ланцюзі має залишок за модулем , сусід по ланцюгу повинен мати залишок

r_2 = (k - r) \bmod k,

інакше сума не дорівнює за модулем . Таким чином, залишки в ланцюзі формуються з комплементарних пар (r, r_2).

Нехай

o = \left\lfloor \frac{m+1}{2} \right\rfloor, \qquad e = \left\lfloor \frac{m}{2} \right\rfloor.

Якщо r \neq r_2, то в ланцюзі залишки повинні чергуватися між та r_2. Є два шаблони: почати з (тоді нам потрібно позицій з залишком та з r_2) або почати з r_2 (тоді з r_2 та з ). Якщо \text{rem}[r] = c_0 та \text{rem}[r_2] = c_1, і обидва існують, способи призначити різні елементи на обмежених позицій дорівнюють

P(c_0,o)\cdot P(c_1,e) \;+\; P(c_1,o)\cdot P(c_0,e).

Якщо r = r_2 (самокомплементарний залишок, наприклад, r=0 або r=k/2, коли парне), тоді всі обмежених позицій повинні використовувати лише залишок , і якщо є c_0 = \text{rem}[r] таких елементів, кількість способів дорівнює P(c_0,m).

Ми перебираємо всі залишки в , обчислюємо r_2 = (k-r)\bmod k і:

  • якщо r = r_2, ми використовуємо cur = P(c_0,m);

  • якщо r < r_2 і r_2 існує, ми використовуємо cur = P(c_0,o)P(c_1,e) + P(c_1,o)P(c_0,e);

  • якщо r > r_2, ми пропускаємо (невпорядкована пара \{r,r_2\} обробляється, коли ми знаходимося на меншому).

Для кожної такої пари ми маємо способів заповнити обмежених позицій. Залишені n-m позицій вільні і можуть бути заповнені залишковими елементами в будь-якому порядку, додаючи фактор (n-m)!. Таким чином, ми накопичуємо

ans \mathrel{+}= cur \cdot (n-m)!.

До цього моменту кожне входження одного й того ж значення розглядається як різний маркований об'єкт. Щоб підрахувати різні перестановки мультимножини , ми ділимо на для кожного різного значення , тобто множимо на invfact[\text{cnt}[x]]. Це те саме виправлення, що й у випадку m=1.

Складність загалом становить \mathcal{O}(\sum n).

MOD=1000000007
MAX=200005

fact=[1]*MAX
invfact=[1]*MAX

for i in range(1,MAX):
    fact[i]=fact[i-1]*i%MOD
    invfact[i]=pow(fact[i],MOD-2,MOD)

def P(n,r):
    if r<0 or r>n:return 0
    return fact[n]*invfact[n-r]%MOD

def solve():
    n,m,k=map(int,input().split())
    rem={}
    cnt={}
    a=list(map(int,input().split()))
    for x in a:
        r=x%k
        rem[r]=rem.get(r,0)+1
        cnt[x]=cnt.get(x,0)+1
    input().split()
    if m==1:
        ans=fact[n]
        for c in cnt.values():
            ans=ans*invfact[c]%MOD
        print(ans)
        return
    o=(m+1)//2
    e=m//2
    ans=0
    for r,c0 in rem.items():
        r2=(k-r)%k
        if r==r2:
            cur=P(c0,m)
        elif r<r2:
            if r2 in rem:
                c1=rem[r2]
                cur=(P(c0,o)*P(c1,e)+P(c1,o)*P(c0,e))%MOD
            else:
                cur=0
        else:
            continue
        ans=(ans+cur*fact[n-m])%MOD
    for c in cnt.values():
        ans=ans*invfact[c]%MOD
    print(ans)


for _ in range(int(input())):
    solve()