DP:
暴力艹标算系列,实际上正解很妙(以前看过),感兴趣的可以去了解一下。
设 d p [ i ] [ j ] [ 0 / 1 ] [ 0 / 1 ] dp[i][j][0/1][0/1] dp[i][j][0/1][0/1]表示 i i i子树选了 j j j个点, i i i是否被选的儿子结点窃听到以及是否选 i i i的方案数。复杂度 O ( n k 2 ) O(nk^2) O(nk2),上界卡不满
瞎写了个fft卷积,跑得比暴力慢许多…
#include
typedef double db;
typedef long long ll;
const int N=1e5+2,M=101,mod=1e9+7;
using namespace std;
int n,m,ans,sz[N];
int head[N],to[N<<1],nxt[N<<1],tot;
int f[N][M][2][2],rep[M][2][2];
char cp;
inline void rd(int &x)
{
cp=getchar();int f=0;x=0;
for(;!isdigit(cp);cp=getchar()) if(cp=='-') f=1;
for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
if(f) x=-x;
}
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
void dfs(int x,int fr)
{
int i,k,t,l,a,b,y;sz[x]=1;
f[x][1][0][1]=f[x][0][0][0]=1;
for(i=head[x];i;i=nxt[i]){
y=to[i];if(y==fr) continue;
dfs(y,x);sz[x]+=sz[y];
for(k=m;k>=0;--k)
for(a=0;a<2;++a)
for(b=0;b<2;++b)
rep[k][a][b]=0;
for(k=min(m,sz[y]);k>=0;--k)
for(t=min(sz[x]-sz[y],m-k);t>=0;--t){
if(f[y][k][0][0])
for(l=0;l<2;++l)
ad(rep[k+t][l][1],(ll)f[x][t][l][1]*f[y][k][0][0]%mod);
if(f[y][k][0][1])
for(l=0;l<2;++l)
ad(rep[k+t][1][1],(ll)f[x][t][l][1]*f[y][k][0][1]%mod);
if(f[y][k][1][0])
for(l=0;l<2;++l){
ad(rep[k+t][l][0],(ll)f[x][t][l][0]*f[y][k][1][0]%mod),
ad(rep[k+t][l][1],(ll)f[x][t][l][1]*f[y][k][1][0]%mod);
}
if(f[y][k][1][1])
for(l=0;l<2;++l){
ad(rep[k+t][1][0],(ll)f[x][t][l][0]*f[y][k][1][1]%mod),
ad(rep[k+t][1][1],(ll)f[x][t][l][1]*f[y][k][1][1]%mod);
}
}
for(k=m;k>=0;--k)
for(a=0;a<2;++a)
for(b=0;b<2;++b)
f[x][k][a][b]=rep[k][a][b];
}
}
int main(){
freopen("action.in","r",stdin);
freopen("action.out","w",stdout);
int i,x,y;rd(n);rd(m);
for(i=1;i<n;++i){rd(x);rd(y);lk(x,y);lk(y,x);}
dfs(1,0);
for(i=0;i<2;++i) ad(ans,f[1][m][1][i]);
printf("%d",ans);
fclose(stdin);fclose(stdout);
return 0;
}
贡献转移+前缀和优化
注意图是一个仙人掌,可以特判环边和树边,并不需要真的跑斯坦纳树(复杂度也不对)。
选一个子图 G G G中的若干个点(至少1个)的方案显然是 2 ∣ G ∣ − 1 2^{|G|}-1 2∣G∣−1。
求期望转成总和除以 2 n 2^{n} 2n,分别考虑每条树边和每个环对答案的贡献:
p.s
注意邻接表边要开4倍
#include
#define pb push_back
typedef long long ll;
using namespace std;
const int N=205,mod=1e9+7;
int n,m,ans,bel[N],bin[N];
int sz[N],dep[N],vs[N],tim,cir;
int ta[N],tb[N],ca,cb,f[N],val[N],fa[N];
int head[N],to[N<<2],nxt[N<<2],tot;
vector<int>g[N];
char cp;
inline void rd(int &x)
{
cp=getchar();int f=0;x=0;
for(;!isdigit(cp);cp=getchar()) if(cp=='-') f=1;
for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
if(f) x=-x;
}
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline int inc(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dec(int x,int y){x-=y;return x<0?x+mod:x;}
void dfs(int x,int fr)
{
int i,j,a,b;vs[x]=tim;
fa[x]=fr;dep[x]=dep[fr]+1;
for(i=head[x];i;i=nxt[i]){
j=to[i];if(j==fr) continue;
if(vs[j]!=tim) dfs(j,x);
else if(j<x){
ca=cb=0;a=x;b=j;
for(cir++;a!=b;){
if(dep[a]<dep[b]) {tb[++cb]=b;b=fa[b];}
else {ta[++ca]=a;a=fa[a];}
}
for(j=1;j<=ca;++j) g[cir].pb(ta[j]);
g[cir].pb(a);
for(j=cb;j>0;--j) g[cir].pb(tb[j]);
for(j=g[cir].size()-1;j>=0;--j) bel[g[cir][j]]=cir;
}
}
}
int gt(int x,int fr)
{
int re=1,i,j;vs[x]=tim;
for(i=head[x];i;i=nxt[i]){
j=to[i];if(j!=fr && vs[j]!=tim) re+=gt(j,x);
}
return re;
}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
inline int fp(int x,int y)
{
int re=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1) re=(ll)re*x%mod;
return re;
}
int main(){
freopen("defense.in","r",stdin);
freopen("defense.out","w",stdout);
int i,j,k,x,y,len;
rd(n);rd(m);bin[0]=1;
for(i=1;i<=n;++i) bin[i]=inc(bin[i-1],bin[i-1]);
for(i=1;i<=m;++i){rd(x);rd(y);lk(x,y);lk(y,x);}
tim++;dfs(1,0);
for(i=1;i<=n;++i) if(!bel[i]) bel[i]=++cir;
for(i=1;i<=n;++i){
for(j=head[i];j;j=nxt[j]) if(bel[to[j]]!=bel[i]){
tim++;x=gt(to[j],i);sz[i]+=x;
if(to[j]>i) ad(ans,(ll)dec(bin[n-x],1)*dec(bin[x],1)%mod);
}
sz[i]=dec(bin[sz[i]+1],1);
}
for(y=1;y<=cir;++y){
len=g[y].size();
for(i=1;i<len;++i)
for(val[i]=0,x=0;x<i;++x){
for(j=0;j<x;++j) f[j]=0;
f[x]=sz[g[y][x]];
for(j=x+1;j<len;++j)
f[j]=inc(f[j-1],(ll)dec(f[j-1],j<i+1?0:f[j-i-1])*sz[g[y][j]]%mod);
ad(val[i],dec(f[len-1],f[len-1-i+x]));
}
for(i=1;i<len;++i)
ad(ans,(ll)(len-i)*dec(val[i],val[i-1])%mod);
}
printf("%d",(ll)ans*fp(2,(ll)n*(mod-2)%(mod-1))%mod);
fclose(stdin);fclose(stdout);
return 0;
}
二分+二分图匹配
二分最远距离 m i d mid mid时,可以得到每个点能够到达的圆上的一段区间,最优解显然可以取到某个点刚好卡在区间边界上的情况——否则整体左/右平移直到某个点卡上。
考虑枚举被卡住的点,其它位置就是固定的了,跑匈牙利判断是否存在完美匹配即可。
但复杂度是 O ( n ⋅ n 3 log w ) O(n·n^3\log w) O(n⋅n3logw)的,膜了claris的题解学习了一发优化技巧:
优化后期望复杂度 O ( ( n + log n log w ) n 3 32 ) O((n+\log n\log w)\dfrac{n^3}{32}) O((n+lognlogw)32n3)
STO Claris
为了好(偷)写(懒),代码调用了很多可能会造成一定精度误差的函数,但这题似乎没卡精度?
#include
using namespace std;
typedef double db;
typedef unsigned int ui;
const int N=205;
const db pi=acos(-1.0),eps=1e-8;
int n,m,R,bel[N],tim;db dlt,ans,mn;
ui nt[N][7],vs[7];
inline db sqr(db x){return x*x;}
struct P{
db x,y;
inline db sq()
{return sqrt(sqr(x)+sqr(y));}
}p[N],pos;
char cp;
inline void rd(int &x)
{
cp=getchar();int f=0;x=0;
for(;!isdigit(cp);cp=getchar()) if(cp=='-') f=1;
for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
if(f) x=-x;
}
int dfs(int x)
{
for(int j,i=0;i<=m;++i){
for(;(vs[i]&nt[x][i]);){
j=i<<5 | __builtin_ctz(vs[i]&nt[x][i]);
vs[i]^=(1U<<(j&31));
if(!bel[j] || dfs(bel[j])){
bel[j]=x;return 1;
}
}
}
return 0;
}
inline db dist(P a,P b)
{return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
inline bool ck(int x,int y,db lim)
{
int i,j;db dis=p[x].sq(),rv=atan2(p[x].y,p[x].x);
rv+=y*acos(((db)R*R+dis*dis-lim*lim)/(2*dis*R));
for(i=1;i<=n;++i)
for(j=0;j<=m;++j)
nt[i][j]=0U;
for(i=0;i+1<n;++i){
rv+=dlt;pos=(P){cos(rv)*R,sin(rv)*R};
for(j=1;j<=n;++j) if(j!=x && dist(p[j],pos)-eps<lim)
nt[j][i>>5]|=1U<<(i&31);
}
memset(bel,0,sizeof(bel));
for(i=1;i<=n;++i) if(i!=x){
for(j=0;j<=m;++j) vs[j]=~0U;
if(!dfs(i)) return false;;
}
return true;
}
int main(){
srand(19260817);
freopen("fleet.in","r",stdin);
freopen("fleet.out","w",stdout);
int i,j,x,y;db nw,l,r,mid;rd(n);rd(R);m=(n-1)>>5;
for(i=1;i<=n;++i){
rd(x);rd(y);p[i]=(P){(db)x,(db)y};
nw=p[i].sq();ans=max(ans,nw);
if(nw<=R) mn=max(mn,R-nw);else mn=max(mn,nw-R);
}ans+=R;
//特判R=0的情况
if(R==0) {printf("%.8lf",ans);return 0;}
dlt=(pi+pi)/n;
random_shuffle(p+1,p+n+1);
for(i=1;i<=n;++i)
for(j=-1;j<=1;j+=2){
l=mn;r=max(min(p[i].sq()+R,ans-eps),mn);
if(ck(i,j,r))
for(;r-l>eps;) {mid=(l+r)/2;if(ck(i,j,mid)) r=(ans=mid);else l=mid;}
}
printf("%.8lf",ans);
fclose(stdin);fclose(stdout);
return 0;
}
总结
三题都在可想可做范围内(暂不讨论T1正解)
250-有点凉,200-就真的凉了。
但是最近代码能力欠佳,小错误不断。