暑假打的是HDU多校,没报牛客,来补补题
Eazy
- 题意:给定(n,m,k),求(sumlimits_{{a_i}{b_i},a>0,b>0,|a|=|b|=k}[sumlimits_{j=1}^k a_j=n][sumlimits_{j=1}^k b_j=m]prodlimits_{j=1}^k min(a_j,b_j))
- 做法:
考虑二元生成函数
(egin{aligned}G(x,y)&=sumlimits_{i,j>0}min(i,j)x^iy^j\ &=sumlimits_{i>0}ix^isumlimits_{jge i}y^j+sumlimits_{i>0}iy^isumlimits_{j>i}x^j\ &=sumlimits_{i>0}ix^iy^i(frac{1}{1-y}+frac{x}{1-x})\ &=frac{xy}{1-xy}(frac{1}{1-y}+frac{x}{1-x})\ &=frac{xy}{(1-xy)(1-x)(1-y)}\ end{aligned})
(Ans=[x^ny^m]G(x,y)^k)
Easy construction
- 题意:给定一棵树,多次询问,给出((l,r,x)),求(sumlimits_{lle i<jle r}[lca(i,j)=x])
- 做法:
即求({size_xchoose 2}sumlimits_{vin son_x}{size_vchoose 2})
考虑链分治,对重儿子直接主席树查询;至于轻儿子,可以对((l,r))离线下来莫队,对于每个点(i),计算其到根路径对祖先的贡献。这样复杂度是(O(nlogn+nsqrt{n}logn))的
但是还不够,考虑对树链剖分变形
对于每个点,令子树大小前(O(sqrt{n}))大的为重儿子,对于每个点,每跳一次轻边,子树大小乘以(O(sqrt{n})),故只会经过(O(1))次轻边
对于重儿子,即查询对于区间([l',r'])有多少个小于等于某个数的个数,共(O(nsqrt{n}))个查询,对([l',r'])差分处理,配合(O(sqrt{n}))修改-(O(1))查询的值域分块
总复杂度(O(nsqrt{n}))
NeoMole Synthesis
- 题意
- 做法:
枚举任意点为(T)的根,由于(sum |T'_i|le 500),讲模板树重新标号,使得不同模板树上无相同节点
令(f_{i,j,k})为(T)中点(i-fa_i)匹配模板树中的(j-k)的最小花费(除正在匹配的这棵树,其他树均匹配完了)。特殊的,若(k=0),则正在匹配的模板树匹配
令(g_i)为(T)中点的子树全部匹配完的最小花费:(g_i=min(f_{i,j,0}))
容易发现(f)的转移是二分图完备匹配,用KM算法。总复杂度(O(n^4))
考虑优化,容易发现(f_{i,j,k_1 eq 0})只用从(f_{i,j,k=0})的二分图中(k_1)退一次流即可
退流的过程是交替走匹配边/非匹配边,用floyd优化。
若将(deg_i,deg_j)一起做,复杂度(sumlimits_{i,j}(deg_i+deg_j)^3=n^4)
我们可以仅对(deg_j)做,对于两个点(x,y),令其匹配点为(x',y'),初始化(dis(x,y)=-w_{x,x'}+w_{y,x'})
则对于退流的两个点(a,b),令(b)的匹配点为(b'),退流的流量为(dis_{a,b}-w_{b,b'})
(sumlimits_{iin T'}deg_i^2frac{n}{deg_i}=O(n^3))
放一份KM-bfs的板子
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define IL inline
typedef long long LL;
const int N = 400 + 3;
const int INF = 0x3f3f3f3f;
struct Kuhn_Munkers {
int n;
int W[N][N];
int Lx[N],Ly[N];
int left[N];
int slack[N];
int pre[N];
bool T[N];
IL void init(int n) {
this->n = n;
for(int i=1;i<=n;i++) fill(W[i],W[i]+1+n,INF);
}
IL void bfs(int u) {
fill(slack,slack+1+n,INF);
fill(pre,pre+1+n,0);
int x,y=0,yy=0,a;
left[y] = u;
for(;;) {
x = left[y]; a = INF, T[y] = true;
for(int i=1;i<=n;i++) if(!T[i]){
if(slack[i] > Lx[x]+Ly[i]-W[x][i]) {
slack[i] = Lx[x] + Ly[i] - W[x][i];
pre[i] = y;
}
if(slack[i] < a) a = slack[i],yy = i;
}
for(int i=0;i<=n;i++) {
if(T[i]) { Lx[left[i]] -= a; Ly[i] += a;}
else slack[i] -= a;
}
y = yy;
if(!left[y]) break;
}
while(y) left[y] = left[pre[y]], y = pre[y];
}
IL int KM() {
fill(Lx,Lx+1+n,0);
fill(Ly,Ly+1+n,0);
fill(left,left+1+n,0);
for(int i=1;i<=n;i++) {
fill(T,T+1+n,false);
bfs(i);
}
int ans = 0LL;
for(int j=1;j<=n;j++) ans += W[left[j]][j];
return ans;
}
}solver;
int n;
int p[N];
LL a[N],b[N],c[N];
int main() {
scanf("%d",&n); solver.init(n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
int v = 0;
for(int k=1;k<=n;k++) if(b[i]+c[j] > a[k]) v += p[k];
solver.W[i][j] = v;
}
}
printf("%d
",solver.KM());
return 0;
}
Disgusting Relationship
- 题意:
令(n)置换环长为(i)的个数为(a_i)个,令(f(a_1,a_2,cdots,a_n))为环长为(i)的个数有(a_i)个的方案数
给定(n,p)((p)为个数),求有多少个序列({a_i})使得(p)不整除(f(a)) - 做法:
(f(a)=dfrac{n!}{prodlimits_{i=1}^n i^{a_i}a_i!})
题目可以转化为最大化分母中质因子(p)的次幂
结论1:若(a_k>0(k>p))则不优
令(a_1=n),可得分母中质因子(p)的次幂最大为(sumlimits_{i=1}^{infty}frac{n}{p^i})
令(a_1=n-frac{n}{p} imes p,a_p=frac{n}{p}),此时次幂为(frac{n}{p}+sumlimits_{i=2}^{infty}frac{n}{p^i})
结论2:令((a_1,a_2,cdots,a_m)_p=n),(a_i(i<m))上的每一个(1)要么在(a_1)要么在(a_p)(根据Kummer定理易证)
(ans=p_{n\%p} imesprodlimits_{i=1}^{m-1}(b_i+1))(其中(p_i)是(i)的分拆数方案数)
Decrement on the Tree
- 题意
给定一棵带边权树,(m)次修改一条边的边权。对于一棵树,每次可以选择一条路径将其权值(-1),求最少多少次可以使树边权全为(0) - 做法
对于一个点,其边可以两两配对
令边权和为(sum),最大边权为(mx),若(mx>sum-mx),贡献为(2mx-sum)。若(mxle sum-mx),若为奇数贡献为(1),否则为(0)