loj 2546 「JSOI2018」潜入行动
容易看出是dp, 如果在节点 \(x\) 放,那么最多影响到 \(x\) 的父亲节点,这与我前几天做到的 abc207f 大同小异,所以,可以得到一个状态 \(dp(i,j,f_1,f_2)\) 表示在节点 \(i\) ,放了 \(j\) 个,是否在结点 \(i\) 上放置,节点 \(i\) 是否被覆盖的方案数. 每次转移是卷积,时间复杂度可以做到 \(O(nk)\) 的.
时间复杂度的证明
首先从abc207f的复杂度来着手,把图构成左兄弟右儿子的二叉树,然后合并的时候复杂度是 \(sz(l)\times sz(r)\) 的,可以把问题转化成,对于每个子树,枚举左右子树两两匹配. 换个角度,考虑两个节点 \(u,v\) 在何时会被两两匹配到,只有在 \(lca(u,v)\) 的时候. 此时时间复杂度就是 \(O(n^2)\) 的.
那么对于此题,合并的复杂度是 \(min(sz(l),k)\times min(sz(r),k)\) 的. 依旧做兄弟右儿子地构成一个二叉树,把问题转化成,对于每个子树,枚举左子树的dfs序的后 \(k\) 个,右子树的dfs序的前 \(k\) 个,然后两两匹配的对数.
发现对于点 \(x\) 来说,可以选择的区间是 \([dfn(x)-2k,dfn(x)+2k]\). 现在需要证明的就是这个区间中的点是否只被选了一次, 当节点 \(x\) 被选中时,是在一个子树的前 \(k\) 位或后 \(k\) 位,在合并之后,它就成为了中间的一段,所以是不可以再被选中的. 那么时间复杂度就是 \(O(nk)\).
有关此题的卡常和优化
优化: 发现如果直径大于 \(k\) 的 \(2\) 倍(大概范围),那么肯定没有合法的方案.
卡常: 可以用 bfs 把整棵树的顺序跑出来,然后和父亲合并.
可以处理出每个节点每种方案可以放置的监听器个数的左边界和右边界.
对于一个数 \(x\) 可以用 long long 存,最后只去一次模,来卡常.
code1:
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int n,k,root;
bool leaf[100010];
int dp[100010][110][2][2],f[110][2][2];
int sz[100010],dep[100010];
vector<int>g[100010];
inline void upd(int &x,int y){
x+=y;x%=mod;
}
void unite(int u,int v){
for(int i=0;i<=min(sz[u],k);i++)for(int j=0;j<=min(sz[v],k);j++){
if(i+j>k)break;
for(int tu1=0;tu1<2;tu1++)for(int tu2=0;tu2<2;tu2++){
for(int tv1=0;tv1<2;tv1++)for(int tv2=0;tv2<2;tv2++){
if((!tv2)&&(!tu1))continue;
upd(f[i+j][tu1][tu2|tv1],1ll*dp[u][i][tu1][tu2]*dp[v][j][tv1][tv2]%mod);
}
}
}
for(int i=0;i<=min(sz[u]+sz[v],k);i++)
for(int t1=0;t1<2;t1++)for(int t2=0;t2<2;t2++){
dp[u][i][t1][t2]=f[i][t1][t2];
f[i][t1][t2]=0;
}
}
void get_d(int x,int fa){
for(int i=0;i<(int)g[x].size();i++){
int to=g[x][i];
if(to==fa)continue;
dep[to]=dep[x]+1;
get_d(to,x);
}
}
void dfs(int x,int fa){
upd(dp[x][0][0][0],1);
upd(dp[x][1][1][0],1);
sz[x]=1;
for(int i=0;i<(int)g[x].size();i++){
int to=g[x][i];
if(to==fa)continue;
dfs(to,x);
unite(x,to);
sz[x]+=sz[to];
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=0;i<n-1;i++){
int u,v;
cin>>u>>v;
u--;v--;
g[u].push_back(v);
g[v].push_back(u);
}
get_d(0,-1);
int maxd=0;
for(int i=0;i<n;i++)maxd=max(maxd,dep[i]);
if(maxd>k*2+10){
cout<<0<<endl;
return 0;
}
dfs(0,-1);
int ans=0;
upd(ans,dp[0][k][0][1]);
upd(ans,dp[0][k][1][1]);
cout<<ans<<endl;
return 0;
}
/*inline? ll or int? size? min max?*/
code2:
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
class edge{public:int to,nxt;}e[200010];
int n,k,cnt=0,st=0,ed=0;
int dp[100010][110][2][2],f[110][2][2];
int Q[100010],fa[100010],sz[100010];
int head[100010],deg[100010];
inline void add_edge(int u,int v){
e[cnt]=(edge){v,head[u]};head[u]=cnt++;deg[u]++;
e[cnt]=(edge){u,head[v]};head[v]=cnt++;deg[v]++;
}
inline void upd(int &x,int y){
x+=y;x%=mod;
}
void bfs(int s){
for(int i=0;i<n;i++)fa[i]=-2;
Q[ed++]=s;fa[s]=-1;
while(st<ed){
int x=Q[st++];
for(int i=head[x];i!=-1;i=e[i].nxt){
if(fa[e[i].to]!=-2)continue;
fa[e[i].to]=x;
Q[ed++]=e[i].to;
}
}
// for(int i=0;i<n;i++)cout<<fa[i]<<" ";cout<<endl;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=0;i<n;i++)head[i]=-1;
for(int i=0;i<n-1;i++){
int u,v;
cin>>u>>v;
u--;v--;
add_edge(u,v);
}
// cout<<"ok?";
bfs(0);
// cout<<"ok!"<<endl;
for(int i=0;i<n;i++){
upd(dp[i][0][0][0],1);
upd(dp[i][1][1][0],1);
sz[i]=1;
}
// for(int i=ed-1;i>=0;i--)cout<<Q[i]<<" ";cout<<endl;
for(int id=ed-1;id>=0;id--){
int y=Q[id],x=fa[y];
if(x<0)continue;
for(int sx=0;sx<=min(sz[x],k);sx++)for(int sy=0;sy<=min(sz[y],k);sy++){
if(sx+sy>k)break;
for(int tx1=0;tx1<2;tx1++)for(int tx2=0;tx2<2;tx2++)
for(int ty1=0;ty1<2;ty1++)for(int ty2=tx1^1;ty2<2;ty2++){
upd(f[sx+sy][tx1][tx2|ty1],
1ll*dp[x][sx][tx1][tx2]*dp[y][sy][ty1][ty2]%mod);
}
}
sz[x]+=sz[y];
for(int i=0;i<=min(sz[x],k);i++)for(int t1=0;t1<2;t1++)for(int t2=0;t2<2;t2++){
dp[x][i][t1][t2]=f[i][t1][t2];
f[i][t1][t2]=0;
// if(dp[x][i][t1][t2])
// cout<<x<<","<<i<<","<<t1<<","<<t2<<","<<dp[x][i][t1][t2]<<endl;
}
}
int ans=0;
upd(ans,dp[0][k][0][1]);
upd(ans,dp[0][k][1][1]);
cout<<ans<<endl;
return 0;
}
/*inline? ll or int? size? min max?*/

浙公网安备 33010602011771号