JZOJ 3301 家族
题目
在一个图中,割掉某些边,使其成为若干个连通块,相应的连通块的个数影响权值,问在使权值和不少于 k k k的情况下,让选择的边的极差最小
分析
按边权从小到大排序,选择一段区间,求出权值和,用并查集维护,时间复杂度 O ( m 2 α ( m ) ) O(m^2\alpha(m)) O(m2α(m))
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=1301;
struct node{
int x,y,w;
bool operator <(const node &t)const{
return w<t.w;
}
}e[N<<2];
int f[N],a[N],siz[N],n,m,lim;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
signed main(){
n=iut(); m=iut(); lim=iut();
for (rr int i=1;i<=n;++i) a[i]=iut();
for (rr int i=1;i<=m;++i) e[i]=(node){iut(),iut(),iut()};
sort(e+1,e+1+m); rr int ans=2147483647;
for (rr int i=1;i<=m;++i){
rr int sum=n*a[1];
for (rr int j=1;j<=n;++j) f[j]=j,siz[j]=1;
for (rr int k=i;k<=m;++k){
if (e[k].w-e[i].w>=ans) break;
rr int fa=getf(e[k].x),fb=getf(e[k].y);
if (fa>fb) fa^=fb,fb^=fa,fa^=fb;
if (fa==fb) continue;
sum+=a[siz[fa]+siz[fb]]-a[siz[fa]]-a[siz[fb]];
f[fa]=fb,siz[fb]+=siz[fa];
if (sum>=lim){
ans=e[k].w-e[i].w;
break;
}
}
}
if (ans==2147483647) printf("T_T"); else printf("%d",ans);
return 0;
}
JZOJ 3302 供电网络
分析
首先是一个显然的最小费用最大流,还有上下界,那就用EK吧,每次只流1的流量,不断动态加边,对于输送到别的城市的费用,每次流过1的流量后就再加一条边 a x 2 + b x − a ( x − 1 ) 2 − b ( x − 1 ) = a x 2 + b x − a x 2 + 2 a x − a − b x + b = 2 a ( x − 1 ) + b ax^2+bx-a(x-1)^2-b(x-1)=ax^2+bx-ax^2+2ax-a-bx+b=2a(x-1)+b ax2+bx−a(x−1)2−b(x−1)=ax2+bx−ax2+2ax−a−bx+b=2a(x−1)+b
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <deque>
#define rr register
using namespace std;
const int inf=200000;
struct node{int y,w,f,next;}e[80001]; bool v[211];
int ls[211],dis[211],rest[211],k=1,ans,m,n,s,t,w[211][211],x[611],y[611],l[611],u[611],a[611],b[611],pre[211];
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
inline void add(int x,int y,int w,int f){
e[++k]=(node){y,w,f,ls[x]} ,ls[x]=k,
e[++k]=(node){x,0,-f,ls[y]},ls[y]=k;
}
inline bool spfa(){
for (rr int i=1;i<=m;++i)
if (w[x[i]][y[i]]==l[i]&&l[i]<u[i])
++l[i],add(x[i],y[i],1,a[i]*(l[i]*2-1)+b[i]);
memset(dis,42,sizeof(dis)); dis[s]=0,v[s]=1;
rr deque<int>q; q.push_front(s);
while (q.size()){
rr int x=q.front(); q.pop_front();
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].w&&dis[e[i].y]>dis[x]+e[i].f){
dis[e[i].y]=dis[x]+e[i].f,pre[e[i].y]=i;
if (!v[e[i].y]){
v[e[i].y]=1;
if (q.size()&&dis[e[i].y]<dis[q.front()]) q.push_front(e[i].y);
q.push_back(e[i].y);
}
}
v[x]=0;
}
if (dis[t]<=0) ans+=dis[t];
return dis[t]<=0;
}
inline void update(){
rr int now=t;
while (now!=s){
rr int x=e[pre[now]^1].y,y=e[pre[now]].y;
--e[pre[now]].w,++e[pre[now]^1].w;
++w[x][y],--w[y][x],now=x;
}
}
signed main(){
n=iut(),m=iut(),s=n+1,t=s+1;
for (rr int i=1;i<=n;++i)
rest[i]=iut(),add(s,i,inf,iut()),add(i,t,inf,iut());
for (rr int i=1;i<=m;++i){
x[i]=iut(),y[i]=iut(),a[i]=iut(),b[i]=iut(),l[i]=iut(),u[i]=iut();
rest[x[i]]-=l[i],rest[y[i]]+=l[i];
ans+=a[i]*l[i]*l[i]+b[i]*l[i],w[x[i]][y[i]]+=l[i],w[y[i]][x[i]]-=l[i];
}
for (rr int i=1;i<=n;++i){
if (rest[i]>0) add(s,i,rest[i],-inf);
else add(i,t,-rest[i],-inf);
}
while (spfa()) update();
ans%=inf,ans=ans<0?ans+inf:ans;
return !printf("%d",ans);
}
JZOJ 3303 洛谷 4841 城市规划
分析
根据弱化版,
f
[
n
]
=
2
n
(
n
−
1
)
/
2
−
∑
i
=
1
n
−
1
f
[
i
]
C
n
−
1
i
−
1
2
(
n
−
i
)
(
n
−
i
−
1
)
/
2
\large f[n]=2^{n(n-1)/2}-\sum_{i=1}^{n-1}f[i]C_{n-1}^{i-1}2^{(n-i)(n-i-1)/2}
f[n]=2n(n−1)/2−i=1∑n−1f[i]Cn−1i−12(n−i)(n−i−1)/2
把这个式子拆开可以得到
f
[
n
]
=
2
n
(
n
−
1
)
/
2
−
(
n
−
1
)
!
∑
i
=
1
n
−
1
f
[
i
]
(
i
−
1
)
!
2
(
n
−
i
)
(
n
−
i
−
1
)
/
2
(
n
−
i
)
!
\large f[n]=2^{n(n-1)/2}-(n-1)!\sum_{i=1}^{n-1}\frac{f[i]}{(i-1)!}\frac{2^{(n-i)(n-i-1)/2}}{(n-i)!}
f[n]=2n(n−1)/2−(n−1)!i=1∑n−1(i−1)!f[i](n−i)!2(n−i)(n−i−1)/2
同除
(
n
−
1
)
!
(n-1)!
(n−1)!并移项可以得到
f
[
n
]
(
n
−
1
)
!
=
2
n
(
n
−
1
)
/
2
(
n
−
1
)
!
−
∑
i
=
1
n
−
1
f
[
i
]
(
i
−
1
)
!
2
(
n
−
i
)
(
n
−
i
−
1
)
/
2
(
n
−
i
)
!
\large \frac{f[n]}{(n-1)!}=\frac{2^{n(n-1)/2}}{(n-1)!}-\sum_{i=1}^{n-1}\frac{f[i]}{(i-1)!}\frac{2^{(n-i)(n-i-1)/2}}{(n-i)!}
(n−1)!f[n]=(n−1)!2n(n−1)/2−i=1∑n−1(i−1)!f[i](n−i)!2(n−i)(n−i−1)/2
∵
2
(
n
−
n
)
(
n
−
n
−
1
)
/
2
(
n
−
n
)
!
=
1
\because\frac{2^{(n-n)(n-n-1)/2}}{(n-n)!}=1
∵(n−n)!2(n−n)(n−n−1)/2=1
∴
2
n
(
n
−
1
)
/
2
(
n
−
1
)
!
=
∑
i
=
1
n
f
[
i
]
(
i
−
1
)
!
2
(
n
−
i
)
(
n
−
i
−
1
)
/
2
(
n
−
i
)
!
\large\therefore\frac{2^{n(n-1)/2}}{(n-1)!}=\sum_{i=1}^n\frac{f[i]}{(i-1)!}\frac{2^{(n-i)(n-i-1)/2}}{(n-i)!}
∴(n−1)!2n(n−1)/2=i=1∑n(i−1)!f[i](n−i)!2(n−i)(n−i−1)/2
这就是一个卷积的模式,可以转换成
H
(
n
)
=
F
(
n
)
G
(
n
)
H(n)=F(n)G(n)
H(n)=F(n)G(n)
那么
F
(
n
)
=
G
(
n
)
−
1
H
(
n
)
F(n)=G(n)^{-1}H(n)
F(n)=G(n)−1H(n),用NTT解决
多项式求逆怎么办,懒得证明了我太菜了,时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rr register
using namespace std;
const int N=131072,mod=1004535809,inv3=334845270;
int f[N<<2],g[N<<2],r[N<<2],jc[N],inv[N],n;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed ksm(int x,int y){
rr int ans=1;
for (;y;y>>=1,x=1ll*x*x%mod)
if (y&1) ans=1ll*ans*x%mod;
return ans;
}
inline signed mo1(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline signed mo2(int x,int y){return x<y?x-y+mod:x-y;}
inline void ntt(int *f,int n,int op){
for (rr int i=0;i<n;++i)
if (i<r[i]) swap(f[i],f[r[i]]);
for (rr int p=2;p<=n;p<<=1){
rr int len=p>>1,w=ksm(~op?3:inv3,(mod-1)/p);
for (rr int i=0;i<n;i+=p)
for (rr int j=i,t=1;j<i+len;++j,t=1ll*t*w%mod){
rr int z=1ll*f[len+j]*t%mod;
f[len+j]=mo2(f[j],z),f[j]=mo1(f[j],z);
}
}
}
int g1[N<<2];
inline void tnt(int *f,int *t,int len1,int len2,int tot){
rr int n=1; for (;n<len1+len2;n<<=1);
for (rr int i=len2;i<n;++i) g1[i]=0;
for (rr int i=0;i<len2;++i) g1[i]=t[i];
rr int inv=ksm(n,mod-2);
for (rr int i=0;i<n;++i) r[i]=(r[i>>1]>>1)|((i&1)?n>>1:0);
ntt(f,n,1),ntt(g1,n,1);
for (rr int i=0;i<n;++i) f[i]=1ll*f[i]*g1[i]%mod;
ntt(f,n,-1); for (rr int i=0;i<tot;++i) f[i]=1ll*f[i]*inv%mod;
for (rr int i=tot;i<n;++i) f[i]=0;
}
int r1[N<<2],r2[N<<2];
inline void invm(int *f,int m){
rr int n=1; for (;n<m;n<<=1);
r2[0]=ksm(f[0],mod-2);
for (rr int p=2;p<=n;p<<=1){
for (rr int i=0;i<p;++i) r1[i]=mo1(r2[i],r2[i]);
tnt(r2,r2,p>>1,p>>1,p),tnt(r2,f,p,p,p);
for (rr int i=0;i<p;++i) r2[i]=mo2(r1[i],r2[i]);
}
for (rr int i=0;i<m;++i) f[i]=r2[i];
}
signed main(){
n=iut()+1;
inv[0]=inv[1]=jc[0]=jc[1]=f[0]=1;
for (rr int i=2;i<n;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for (rr int i=2;i<n;++i) inv[i]=1ll*inv[i-1]*inv[i]%mod,jc[i]=mo1(jc[i-1],jc[i-1]);
for (rr int i=3;i<n;++i) jc[i]=1ll*jc[i-1]*jc[i]%mod;
for (rr int i=1;i<n;++i) f[i]=1ll*jc[i]*inv[i]%mod,g[i]=1ll*jc[i]*inv[i-1]%mod;
invm(f,n); rr int m=1; for (;m<=(n<<1);m<<=1); tnt(f,g,m,m,m);
return !printf("%lld",1ll*f[n-1]*ksm(inv[n-2],mod-2)%mod);
}

本文精选三道算法竞赛题目,包括家族题目分析、供电网络分析和城市规划问题,详细解析了每道题目的解题思路与代码实现,涵盖并查集、最小费用最大流和多项式求逆等高级算法。
127

被折叠的 条评论
为什么被折叠?



