ZOJ 3874 Permutation Graph (分治NTT优化DP)

题面:vjudge传送门 ZOJ传送门

题目大意:给你一个排列,如果两个数构成了逆序对,就在他们之间连一条无向边,这样很多数会构成一个联通块。现在给出联通块内点的编号,求所有可能的排列数

推来推去容易发现性质,同一联通块内的点一定是连续标号的,否则无解

然后我就不会了

好神的$NTT$优化$DP$啊

根据上面的性质,联通块之间是互不影响的,所以我们对每个联通块分别统计答案再相乘

定义$f[i]$表示$i$个点构成的合法联通块,可能的排列数

一个合法联通块的所有元素一定在同一联通块内,说明不可能存在两个联通块,因此

$f[i]=i!-\sum f[j]*(i-j)!$

发现这是一个卷积的形式,用分治$NTT$求解即可

模数是一个原根是10的费马素数

别忘了判断无解的情况

  1 #include <cmath>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define N1 (1<<18)+10
  6 #define il inline
  7 #define dd double
  8 #define ld long double
  9 #define ll long long
 10 using namespace std;
 11
 12 const int inf=0x3f3f3f3f;
 13 const ll p=786433;
 14 int gint()
 15 {
 16     int ret=0,fh=1;char c=getchar();
 17     while(c<‘0‘||c>‘9‘){if(c==‘-‘)fh=-1;c=getchar();}
 18     while(c>=‘0‘&&c<=‘9‘){ret=ret*10+c-‘0‘;c=getchar();}
 19     return ret*fh;
 20 }
 21 ll qpow(ll x,ll y)
 22 {
 23     ll ans=1;
 24     for(;y;x=x*x%p,y>>=1) if(y&1) ans=ans*x%p;
 25     return ans;
 26 }
 27
 28 namespace NTT{
 29
 30 ll a[N1],b[N1],c[N1],invwn[N1],mulwn[N1];
 31 int r[19][N1];
 32 void Pre(int len,int L)
 33 {
 34     int i,j;
 35     for(j=1;j<=L;j++) for(i=0;i<(1<<j);i++)
 36         r[j][i]=(r[j][i>>1]>>1)|((i&1)<<(j-1));
 37     for(i=2;i<=len;i<<=1) mulwn[i]=qpow(10,(p-1)/i), invwn[i]=qpow(mulwn[i],p-2);
 38 }
 39 void NTT(ll *s,int len,int type,int L)
 40 {
 41     int i,j,k; ll wn,w,t;
 42     for(i=0;i<len;i++) if(i<r[L][i]) swap(s[i],s[r[L][i]]);
 43     for(k=2;k<=len;k<<=1)
 44     {
 45         wn=(type>0)?mulwn[k]:invwn[k];
 46         for(i=0;i<len;i+=k)
 47         {
 48             for(j=0,w=1;j<(k>>1);j++,w=w*wn%p)
 49             {
 50                 t=w*s[i+j+(k>>1)]%p;
 51                 s[i+j+(k>>1)]=(s[i+j]+p-t)%p;
 52                 s[i+j]=(s[i+j]+t)%p;
 53             }
 54         }
 55     }
 56 }
 57 void Main(int len,int L)
 58 {
 59     int i,invl=qpow(len,p-2);
 60     NTT(a,len,1,L); NTT(b,len,1,L);
 61     for(i=0;i<len;i++) c[i]=a[i]*b[i]%p;
 62     NTT(c,len,-1,L);
 63     for(i=0;i<len;i++) c[i]=c[i]*invl%p;
 64 }
 65 void clr(int sz)
 66 {
 67     memset(a,0,sz<<3);
 68     memset(b,0,sz<<3);
 69 }
 70
 71 };
 72
 73 using NTT::a; using NTT::b; using NTT::c;
 74 ll f[N1],g[N1]; int de;
 75 void CDQ(int l,int r)
 76 {
 77     if(r-l<1) return;
 78     if(r-l==1){ f[l]=(g[l]+p-f[l])%p; return; }
 79     int mid=(l+r)>>1,i,len,L;
 80     CDQ(l,mid);
 81     for(len=1,L=0;len<(mid-l)+(r-l)-1;len<<=1,L++);
 82     for(i=l;i<mid;i++) NTT::a[i-l]=f[i];
 83     for(i=0;i<(r-l);i++) NTT::b[i]=g[i];
 84     NTT::Main(len,L);
 85     for(i=mid;i<r;i++) f[i]=(f[i]+NTT::c[i-l])%p;
 86     NTT::clr(len);
 87     CDQ(mid,r);
 88 }
 89 int T,n,m;
 90 int que[N1];
 91
 92 int main()
 93 {
 94     int i,j,x,y,len,L,mi,ma; ll ans;
 95     scanf("%d",&T); n=100001;
 96     for(i=2,g[1]=1;i<n;i++) g[i]=g[i-1]*i%p;
 97     for(len=1,L=0;len<n+n-1;len<<=1,L++);
 98     NTT::Pre(len,L);
 99     CDQ(0,n);
100
101     while(T--){
102
103     n=gint(); m=gint(); ans=1;
104     for(i=1;i<=m;i++)
105     {
106         x=gint(); mi=inf,ma=0;
107         for(j=1;j<=x;j++) que[j]=gint(), mi=min(mi,que[j]), ma=max(ma,que[j]);
108         if(ma-mi+1!=x) ans=0;
109         ans=(ans*f[x])%p;
110     }
111     printf("%lld\n",ans);
112
113     }
114     return 0;
115
116 }  

原文地址:https://www.cnblogs.com/guapisolo/p/10353943.html

时间: 2024-07-30 16:27:25

ZOJ 3874 Permutation Graph (分治NTT优化DP)的相关文章

ZOJ 3874 Permutation Graph 分治NTT

Permutation Graph Time Limit: 2 Seconds      Memory Limit: 65536 KB Edward has a permutation {a1, a2, … an}. He finds that if he connects each pair (ai, aj) such that i < j and ai > aj, he will get a graph. For example, if the permutation is {2, 3,

HDU 5322 Hope (分治NTT优化DP)

题面传送门 题目大意: 假设现在有一个排列,每个数和在它右面第一个比它大的数连一条无向边,会形成很多联通块. 定义一个联通块的权值为:联通块内元素数量的平方. 定义一个排列的权值为:每个联通块的权值之积 求长度为$n$所有排列的权值之和,$n\leq 1e5$,$1e4$组询问 原题面描述不清楚啊..害得我白想了30min 和ZOJ3874一样都是排列$DP$问题 $DP$方程还是不难想的 假设现在有一个$i-1$的排列,当我们把$i$某个位置上时 $i$前面的数都会和$i$连通,$i$后面的数

HDU 5279 YJC plays Minecraft (分治NTT优化DP)

题目传送门 题目大意:有$n$个小岛,每个小岛上有$a_{i}$个城市,同一个小岛上的城市互相连接形成一个完全图,第$i$个小岛的第$a_{i}$个城市和第$i+1$个小岛的第$1$个城市连接,特别地,第$n$个小岛的第$a_{n}$个城市和第$1$个小岛的第$1$个城市连接.现在要断掉图中的一些边,保证任意两个城市只有一条路径或者不连通,求合法的断边方案总数,$n,a_{i}<=1e5$ 完全不会(喷血 我们对每个小岛单独讨论 如果任意两个城市只有一条路径或者不连通,那么这张图只能是一个森林

【uoj#244】[UER #7]短路 CDQ分治+斜率优化dp

题目描述 给出 $(2n+1)\times (2n+1)$ 个点,点 $(i,j)$ 的权值为 $a[max(|i-n-1|,|j-n-1|)]$ ,找一条从 $(1,1)$ 走到 $(2n+1,2n+1)$ 的路径,使得经过的点(包括起点和终点)权值和最小.求这个权值和. 输入 第一行一个正整数 $n$ . 第二行 $n+1$ 个正整数 $a[0],a[1],…,a[n]$ ,表示从内到外每层的中继器的延时值. 输出 输出一行一个数表示改造后的最短引爆时间. 样例输入 99 5 3 7 6 9

【BZOJ-3672】购票 树分治 + 斜率优化DP

3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1177  Solved: 562[Submit][Status][Discuss] Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.

BZOJ 1492 NOI 2007 货币兑换Cash CDQ分治+斜率优化DP

题目大意:有两种金券,A和B.每一天有一个rate值,表示购入的比例:还有每一天AB金券的售价.现在给出初始的钱数,问最后能够获得多少钱. 思路:这算是神题了吧,啃论文啃别人代码将近一天才算有点明白. 首先题目中说的可以买一部分或者卖一部分是扯淡的,因为为了最大获利一定要全部买入,全部卖出.朴素的DP方程就好弄了. 设f[i]为第i天最多的B券的数量.那么f[i] = (rate[j] * f[j] * a[i] + f[j] * b[i]) / (rate[i] * a[i] + b[i])

bzoj3672/luogu2305 购票 (运用点分治思想的树上cdq分治+斜率优化dp)

我们都做过一道题(?)货币兑换,是用cdq分治来解决不单调的斜率优化 现在它放到了树上.. 总之先写下来dp方程,$f[i]=min\{f[j]+(dis[i]-dis[j])*p[i]+q[i]\} ,j是i的祖先,dis[i]-dis[j]<=l[i]$ ,其中dis[i]表示1号点到i号点的距离 可以很明显的看出斜率优化,但我们要放到树上做 于是就运用点分治的思想来找重心(正如普通的cdq是找重点一样) 步骤是这样的: 1.对于根为x的一个子树,我们先找到它的重心rt 2.把rt的子树刨掉

bzoj1492[NOI2007]货币兑换Cash cdq分治+斜率优化dp

1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 5541  Solved: 2228[Submit][Status][Discuss] Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个实数.每天随着市场的起伏波动, 两种金券都有自己当时的价值,即每一单位金

Codeforces1096G Lucky Tickets(NTT优化dp)

设\(f[i][j]\)表示填了\(i\)个数,数位和为\(j\)的方案数 于是方程为: \[f[i][j]=\sum_{k=1}^9 f[i-1][j-k]*[CanUse[k]==1]\] 其中\(CanUse[i]\)表示是否可用\(i\)这个数字 最终答案为: \[\sum_{i=1}^{9*(n/2)}f[n/2][j]\] 直接转移肯定\(T\)飞,需要一些优化.于是我们观察到这个式子是卷积形式的式子,直接上\(NTT\)板子+快速幂即可 \(P.S.\)一些优化 可以把每个\(po