牛客
]
NC108306 2025牛客暑期多校训练营9
开场速过签到 M,队友速过 J。
开 F(-2),很快得出距离是切比雪夫距离加上些常数。
细节太多分讨写不过来,吃了两发 WA。
其实只需要把 $6$ 个方向的木棍拿出来,然后交换 $l_1,l_2$ 各调试一遍就能少吃一发 WA。
更好写的方法是只需要木棍中点坐标的切比雪夫距离,然后判断横竖。
开 A(-1),注意到 AVL 深度不太高,所以 $f_{i,j}$ 表示 $i$ 子树内深度为 $j$ 即可,转移不难。
同时还要预处理出一个点要加多少点才能变成深度为 $k$ 的 AVL。那么 $g_k=g_{k-1}+g_{k-2}+2$。
在初始化叶子节点的时候不小心写错了,吃一发 WA。
差不多我写完后,队友过 B(-1) 答辩自动机。
然后队友写完 G 这个欧拉数与插板法。
我开 C*,发现是状压,并且注意到每次选出子集必须是排序后的一个前缀。
可惜被细节卡了,没写出来,可惜。
好写的方法是记录每个点的所有比他小的,放到 pre
里,那么 pre[p]
里面的所有点必须都在 k
里面,否则这个点走不到。
复杂度看似是 $\mathcal O(n2^n)$,实际上只需 $\mathcal O(2^n)$。
令 $G(k)$ 表示 $k$ 个点的时间复杂度,由于每次只能选前缀,所以 $G(k)<\sum\limits_{i<k}G(i)\sim\mathcal O(2^n)$。
最终过 $6$ 题,名次 $266$。
#include<bits/stdc++.h>
#define siz(x) int((x).size())
#define all(x) std::begin(x),std::end(x)
#define fi first
#define se second
using namespace std;
using unt=unsigned;
using loli=long long;
using lolu=unsigned long long;
using pii=pair<int,int>;
mt19937_64 rng(random_device{}());
constexpr int N=25,inf=0x3f3f3f3f;
int n,m,f[1<<N],w[N],id[N],pre[N];
basic_string<int>b;
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(false);cin.tie(nullptr);
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>w[i];
id[i]=i;
}
sort(id,id+n,[](int x,int y){
return w[x]<w[y];
});
for(int i=0,u,v;i<m;i++){
cin>>u>>v;
u--;v--;
pre[v]|=(1<<u);
}
for(int i=0;i<n*2;i++)for(int x=0;x<n;x++)for(int y=0;y<n;y++)
if(pre[y]>>x&1)pre[y]|=pre[x];
memset(f,0x3f,sizeof f);
f[0]=0;
for(int k=0;k<(1<<n);k++)if(f[k]!=inf){
b.clear();
for(int j=0;j<n;j++)if(!(k>>id[j]&1))
if((pre[id[j]]&k)==pre[id[j]])b.push_back(id[j]);
for(int j=0,op=0;j<siz(b);j++){
op|=(1<<b[j]);
f[k^op]=min(f[k^op],f[k]+w[b[j]]);
}
}
cout<<f[(1<<n)-1];
return 0;
}