Dim Sum
.
.
解法:首先注意一点,就是每一个人对食物的要求都可以映射到编号为1的人身上,例如有5个人,第2个人想要第3道菜,就相当于第1个人想要第2道菜。于是所有的要求都可以映射到第一个人身上,然后剩下的就是细节的处理了。设当前长度为i,第 i 个食物的要求数为 a[i] :
1、对于要转的距离不超过周长一半:那么方案数为 第i位必选,1~(i-1)位随便选的数量。
2、对于超过一半的距离:那么先假设 i 的对称点为 j , 方案数为 第 i 位必选, 第2到第 j-1 位至少选一位, 然后第1位以及第 i-1 位到第j位随便选的数量
3、这样经过顺序、逆序算完之后长度超过一半的方案数会出现重复,只要减去一次就好了, 为第 i 位和第 j 位必选,第 i-1 为到第 j+1 位任选, 第一位任选的方案数
.
.
.
#include <cstdio>
#include <iostream>
#include <complex>
#include <cmath>
#include <set>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=500000+5;
const int MOD=1000000009;
int n;
int a[maxn], b[maxn], pre[maxn];
LL p2[maxn];
LL work(int a[], bool op) {
LL ret=0;
pre[0]=0;
for (int i=1; i<=n; i++) {
pre[i]=pre[i-1]+a[i];
int j=n+2-i;
if (j>i) {
ret+=((p2[pre[i-1]]*(p2[a[i]]-1))%MOD*(i-1))%MOD;
ret%=MOD;
} else if (j == i) {
ret+=(p2[pre[i-1]])*(p2[a[i]]-1)%MOD*(i-1)%MOD;
ret%=MOD;
if (op) {
LL tmp=(p2[a[1]])*(p2[a[i]]-1)%MOD*(i-1)%MOD;
ret = (ret+MOD-tmp)%MOD;
}
} else {
LL tmp=(p2[pre[j]-pre[1]]-1)*(p2[a[1]])%MOD*(p2[pre[i-1]-pre[j]])%MOD*(p2[a[i]]-1)%MOD;
tmp%=MOD;
tmp*=(i-1); tmp%=MOD;
ret=(ret+tmp)%MOD;
if (op) {
tmp = (p2[a[i]]-1)*(p2[a[j]]-1)%MOD*(p2[pre[i-1]-pre[j]])%MOD*(i-1)%MOD*(p2[a[1]])%MOD;
ret = (ret+MOD-tmp)%MOD;
}
}
}
return ret;
}
void init() {
p2[0]=1;
for (int i=1; i<maxn; i++) p2[i]=(p2[i-1]*2)%MOD;
}
int main() {
//freopen("dim.in","r",stdin);
init();
scanf("%d", &n);
for (int i=0; i<n; i++) {
int x; scanf("%d", &x);
while (x--) {
int y; scanf("%d", &y);
y--;
int pos=(y-i+n)%n+1;
a[pos]++;
if (pos > 1) b[n+2-pos]++;
else b[pos]++;
}
}
printf("%lld\n", (work(a, 0)+work(b, 1))%MOD);
}