按照如下方式生成两棵树:
要求对于所有节点 \(i\),\(i\) 恰好是两棵树中的一个的叶子节点。一个节点被称为叶子当且仅当没有节点的父亲是它。两种方案不同当且仅当存在一棵树中的一个节点 \(i\),两种方案中它的父亲不同。
对于 \(n\in[2,N]\),计算方案数 \(\bmod m\)。
数据范围:\(n\le 500\)。
设 \(f(S)\) 表示第一棵树中,叶子集合恰好为 \(S\) 的方案数,\(g(S)\) 表示第二棵树中,叶子集合恰好为 \(S\) 的方案数。为了方便容斥,设 \(f'(S)\) 表示第一棵树中,叶子集合为 \(S\) 的超集的方案数,\(g'(S)\) 表示第二棵树中,叶子集合为 \(S\) 的超集的方案数。
那么
\[f(S)=\sum_{S\subseteq s}(-1)^{|s|-|S|}f'(s)\\ g(S)=\sum_{S\subseteq s}(-1)^{|s|-|S|}g'(s)\\ \]答案是(设 \(U\) 为全集 \(1\sim n\))
\[\begin{aligned} ans&=\sum_S f(S)g(U\setminus S)\\ &=\sum_S\left(\sum_{S\subseteq s}(-1)^{|s|-|S|}f'(s)\right)\left(\sum_{U\setminus S\subseteq t}(-1)^{|U\setminus S|-|t|}g'(t)\right)\\ &=\sum_S\left(\sum_{S\subseteq s}(-1)^{|s|-|S|}f'(s)\right)\left(\sum_{t\in S}(-1)^{|S|-|t|}g'(U\setminus t)\right)\\ &=\sum_{s}f'(s)\sum_{t\subseteq s}g’(U\setminus t)\sum_{t\subseteq S\subseteq s}(-1)^{|S|-|s|}(-1)^{|S|-|t|}\\ &=\sum_{s}f'(s)\sum_{t\subseteq s}g'(U\setminus t)(-2)^{|s|-|t|} \end{aligned} \]最后一步转化是 \((-1)^{|S|-|s|}(-1)^{|S|-|t|}=(-1)^{|s|-|t|}\),合法的 \(S\) 共 \(2^{|s|-|t|}\) 个。
考虑 DP 维护转移系数。\(i\) 对 \(f'(s)\) 的贡献是 \(j\in[1,i-1],j\not \in s\) 的 \(j\) 的数量,\(i\) 对 \(g'(U\setminus s)\) 的贡献是 \(j\in[i+1,n],j\in s\) 的 \(j\) 的数量。那么设 DP 状态 \(f_{i,x,y}\) 表示考虑完前 \(i\) 个点,已经放在 \(s\) 中的点的数量为 \(x\),钦定后面有 \(y\) 个点在 \(t\) 集合内的 \(\sum_{s}f'(s)\sum_{t\subseteq s}g'(U\setminus t)(-2)^{|s|-|t|}\)。
转移有:
\[f_{i,x,y}\gets f_{i-1,x,y}\times x\times y\\ f_{i,x+1,y-1}\gets f_{i-1,x,y}\times x\times (y-1)\\ f_{i,x+1,y}\gets -2\times f_{i-1,x,y}\times x\times y \]分别表示 不属于 \(s\)、属于 \(s\) 也属于 \(t\),只属于 \(s\) 不属于 \(t\)。空间可以滚动一下。
复杂度 \(O(n^3)\)。
#include <bits/stdc++.h> using namespace std; const int N=505; int mod; int f[2][N][N],n; int main(){ cin>>n>>mod; int pre=0,cur=1; for(int i=1;i<=n;++i)f[0][0][i]=1; for(int i=1;i<=n;pre^=1,cur^=1,++i){ memset(f[cur],0,sizeof(f[cur])); int ans=0; for(int j=0;j<i;++j){ for(int k=0;k<=n-i+1;++k){ int l=(i==1)?1:i-1-j; f[cur][j][k]=(f[cur][j][k]+1ll*f[pre][j][k]*l*k)%mod; if(k==1)ans=(ans+1ll*f[pre][j][k]*l)%mod; if(i>1){ if(k)f[cur][j+1][k-1]=(f[cur][j+1][k-1]+1ll*f[pre][j][k]*l*(k-1))%mod; f[cur][j+1][k]=(f[cur][j+1][k]-2ll*f[pre][j][k]*l*k)%mod; } } } if(i>1)printf("%d\n",(ans+mod)%mod); } return 0; }
给你序列 \(a_1,a_2,\cdots a_n\)。你可以进行一次操作,选择一个区间 \([l,r](1\le l\le r\le n)\) 和一个整数 \(k\),将区间 \([l,r]\) 的数 \(+k\)。询问操作之后众数的出现次数的最大值,并输出这个众数的所有可能取值。
数据范围:\(T\) 组数据,\(T\le 20,2\le n\le 2\times 10^5,\sum n\le 5\times 10^5,1\le a_i\le 10^9,\) 所有 \(a_i\) 不全相等。
我们最终的答案一定是选择一个有序对 \((x,y)\),然后选择一个区间,把这个区间内的 \(y\) 全变成 \(x\)(此时这个区间内的 \(x\) 会变成一个 \(\neq x\) 的数),让众数变成 \(x\)。
考虑根号分治。
枚举所有出现次数 \(\ge \sqrt{n}\) 的 \(i\)。现在我们要对所有 \(j\) 求出以下两个东西:
这两个东西都可以 \(O(n)\) 扫一遍,维护每个不同的 \(j\) 的前缀和的最小值求出。
因为出现次数 \(\ge \sqrt{n}\) 的数的数量 \(\le \sqrt{n}\),这一部分的复杂度是 \(O(n\sqrt{n})\)。
我们要求的还是:原序列中,\(i\) 的权值为 \(-1\),\(j\) 的权值为 \(1\),其它位置权值为 \(0\) 的最大子段和(此时众数为 \(j\))。
因为 \(i\) 的出现次数 \(\le \sqrt{n}\),我们找出 \(i\) 的所有区间(这样我们知道里面有多少个 \(-1\),这样的区间最多 \(O(n\sqrt{n})\) 个),现在的问题是 \(O(n\sqrt{n})\) 个区间的区间众数。
直接求区间众数没法快速求出,注意到这个区间众数一定 \(\le \sqrt{n}\)(因为 \(y\) 的出现次数 \(\le\sqrt{n}\)),我们暴力枚举这个区间众数是多少,设为 \(p\),那么我们对每个右端点 \(r\),双指针找到一个最靠右的左端点 \(l\),满足 \([l,r]\) 的区间众数为 \(p\)。我们就能回答所有右端点为 \(r\),左端点 \(\le l\) 的所有询问了。
这一部分的复杂度也是 \(O(n\sqrt{n})\)。
总复杂度 \(O(n\sqrt{n})\)。
#include <bits/stdc++.h> using namespace std; inline int read(){ int s=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar(); return s*f; } const int N=2e5+5; int a[N],b[N],n,B,T,mx[N],s[N],tcnt[N],tot; vector<int> ans; template<typename T>inline void Max(T&a,T b){if(a<b)a=b;} template<typename T>inline void Min(T&a,T b){if(a>b)a=b;} namespace sub1{ int buc[N],t,s[N],mn[N],mn1[N]; bool tag[N]; inline void work(int c){ t=0; for(int i=1;i<=tot;++i)tag[i]=1,mn[i]=0,mn1[i]=0,s[i]=0,buc[i]=i; t=tot; int cnt=0; for(int i=1;i<=n;++i){ if(a[i]==c){ for(int j=1;j<=t;++j)tag[buc[j]]=0; ++cnt;t=0; }else{ const int col=a[i]; ++s[col]; if(!tag[col]){ Min(mn[col],s[col]-1-cnt); tag[col]=1; buc[++t]=col; } Max(mx[col],cnt-s[col]+1-mn1[col]+tcnt[col]); Max(mx[c],s[col]-cnt-mn[col]+tcnt[c]); Min(mn[col],s[col]-cnt); Min(mn1[col],cnt-s[col]); } } for(int i=1;i<=tot;++i)if(i!=c)Max(mx[i],cnt-s[i]-mn1[i]+tcnt[i]); } } int curmx; namespace sub2{ vector<int> buc[N]; int cnt[N],ccnt[N],app; inline void insert(int x){ --ccnt[cnt[x]]; ++ccnt[++cnt[x]]; Max(app,cnt[x]); } inline void del(int x){ --ccnt[cnt[x]]; ++ccnt[--cnt[x]]; if(!ccnt[app])--app; } int L[N],rk[N],L1[N]; inline void work(){ for(int i=B;i>=1&&i+B>=curmx;--i){ int r=0,l=1; app=0; memset(cnt+1,0,tot<<2); memset(ccnt+1,0,n<<2); ccnt[0]=tot; while(r<=n&&app<i) insert(a[++r]); if(app<i)continue; del(a[r]); for(;r<=n;++r){ insert(a[r]); while(app>=i)del(a[l++]); insert(a[--l]); if(r==n){ for(int c=1;c<=tot;++c){ if(tcnt[c]>=B)continue; while(L1[c]<=tcnt[c]&&buc[c][L1[c]]<=l){ Max(mx[c],app-(tcnt[c]-L1[c])+tcnt[c]); Max(curmx,mx[c]); ++L1[c]; } } break; } int c=a[r+1]; if(tcnt[c]>=B)continue; while(L[r+1]<rk[r+1]&&buc[c][L[r+1]]<=l){ Max(mx[c],app-(rk[r+1]-L[r+1]-1)+tcnt[c]); Max(curmx,mx[c]); ++L[r+1]; } } } } inline void init(){ for(int i=1;i<=tot;++i){ buc[i].clear(); buc[i].push_back(0); L1[i]=0; } for(int i=1;i<=n;++i)L[i]=0,buc[a[i]].push_back(i),rk[i]=buc[a[i]].size()-1; } } int main(){ T=read(); while(T--){ n=read(); for(int i=1;i<=n;++i)a[i]=read(),b[i]=a[i]; tot=n;curmx=0; sort(b+1,b+1+tot); tot=unique(b+1,b+1+tot)-b-1; for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+1+tot,a[i])-b; for(int i=1;i<=tot;++i)mx[i]=0,tcnt[i]=0; for(int i=1;i<=n;++i)++tcnt[a[i]]; B=sqrt(n); for(int i=1;i<=tot;++i)if(tcnt[i]>=B)sub1::work(i); for(int i=1;i<=tot;++i)Max(curmx,mx[i]); sub2::init(); sub2::work(); int maxx=0; for(int i=1;i<=tot;++i)Max(maxx,mx[i]); printf("%d\n",maxx); for(int i=1;i<=tot;++i) if(mx[i]==maxx)printf("%d\n",b[i]); } return 0; }
(写不动简要题意了)
九条可怜是一个喜欢计算几何的女孩子,她画了一个特别的平面坐标系,其中 xx 轴正半轴与 yy 轴正半轴夹角为 \(60\) 度。
从中,她取出所有横纵坐标不全为偶数,且满足 \(-2 a + 1 \le x \le 2 a - 1\),\(-2 b + 1 \le y \le 2 b - 1\),\(-2 c + 1 \le x + y \le 2 c - 1\) 的整点。
可怜想将其中一些点染色,但相邻的点不能同时染色。具体地,对于点 \((x,y)\),它和 \((x, y + 1), (x, y - 1), (x + 1, y), (x - 1, y), (x + 1, y - 1), (x - 1, y + 1)\) 六个点相邻,可结合样例解释理解。
可怜想知道在这个规则下最多能将多少点染色,以及染最多点的染色方案数。由于后者值可能很大,对于染色方案数,你只需要输出对 998244353 取模后的结果。注意不需要将最多染色点数取模。
(其中 A,Q,H 三点是被删除的,不能被选择)
数据范围:\(1\le a,b,c\le 10^6\),\(T\) 组数据,\(1\le T\le 10\)
把所有横纵坐标都是偶数的点连出变成为 \(2\) 的等边三角形,容易发现,所有没被删除的点都是一个等边三角形某一边的中点。把两个有公共边的等边三角形连边,这条边就表示这个边上的中点选不选
原问题中,两个点不能相邻的条件相当于新的图上,两条被选择的边不能有公共点。即一个最大匹配计数。
容易发现,这个题转化之后与 [Cnoi2021]六边形战士。其中原文题的 \(A=\max(0,a+b-c),B=\max(0,a+c-b),C=\max(0,b+c-a)\)。ZJOI 这题还需要求最大匹配的大小,这个最大匹配是完美匹配,所以最大匹配是 \(A\times B+B\times C+A\times C\)。
#include <bits/stdc++.h> using namespace std; const int N=1e7+5,mod=998244353; int T; inline int Inv(int a){ int ret=1,b=mod-2; for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ret=1ll*ret*a%mod; return ret; } int fac[N],h[N]; inline int calc(int a,int b,int c){ int n=a+b+c; return 1ll*h[a]*h[b]%mod*h[c]%mod*h[n]%mod*Inv(1ll*h[a+b]*h[b+c]%mod*h[a+c]%mod)%mod; } int a[100],b[100],c[100],mx; inline void init(int n){ fac[0]=1;h[0]=1; for(int i=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mod,h[i]=1ll*h[i-1]*fac[i-1]%mod; } int main(){ cin>>T; for(int i=1;i<=T;++i){ cin>>a[i]>>b[i]>>c[i]; mx=max(mx,a[i]+b[i]+c[i]); } init(mx); for(int i=1;i<=T;++i){ a[i]=min(a[i],b[i]+c[i]); b[i]=min(b[i],a[i]+c[i]); c[i]=min(c[i],a[i]+b[i]); int a1=a[i]+b[i]-c[i],b1=a[i]+c[i]-b[i],c1=b[i]+c[i]-a[i]; printf("%lld %d\n",1ll*a1*b1+1ll*b1*c1+1ll*a1*c1,calc(a1,b1,c1)); } return 0; }