D
思维 k进制
从一个不超过300个元素的集合,选最多三个数加起来,能拼出 [ 1 , 1 e 6 ] [1,1e6] [1,1e6]中的所有数字,求这个集合?
集合里可用的不同数字很少,但是要拼出很大范围的数字,显然考虑k进制。
注意到需要表示 1 e 6 1e6 1e6的数字,并且只能用三个数字,也就是在 k k k进制下,只用三位就能表示 1 e 6 1e6 1e6,那么显然可以 100 100 100进制。然而问题是100进制每一位不只是0或1,每一位元素,可能要重复多次才能拼出这一位的数字,比如十进制下,9确实可以只用 1 0 0 = 1 10^0=1 100=1拼出来,但是需要9个1,而我们这里只能用三个数字。也就是每一位只能用一个数字。
再考虑到集合元素可以有300个,并不是只允许 [ 1 , 100 , 10000 ] 三个元素 [1,100,10000]三个元素 [1,100,10000]三个元素,可以保存 100 , 200 , . . . 100,200,... 100,200,...所有元素,这样100进制下每一位的每个数字,都可以用一个元素表示。正好用完300个的限制
void solve(){
cout<<300<<'\n';
rep(i,1,100){
cout<<i<<' ';
}
rep(i,1,100){
cout<<i*100<<' ';
}
rep(i,1,100){
cout<<i*10000<<' ';
}
}
E
环dp
一个环,每次可以付出一个代价选中条边,会把边两端的点选中,问选中所有点至少一次的最小代价
如果这只是个序列,显然状态机dp, d p ( i , 0 / 1 ) dp(i,0/1) dp(i,0/1)表示前i个点都已经选中,且 ( i , i + 1 ) (i,i+1) (i,i+1)这条边是否选中的最小代价
但这是个环,那么可以枚举点 ( n , 1 ) (n,1) (n,1)这条边是否被选中,对这两种情况,环的性质被破坏了,分别跑链上的dp,去最值就是答案
void solve(){
int n;
cin>>n;
vvi dp(n+1,vi(2,1e18));
vi a(n+1);
rep(i,1,n){
cin>>a[i];
}
dp[1][1]=a[1];
rep(i,2,n){
dp[i][0]=dp[i-1][1];
dp[i][1]=min(dp[i-1][0],dp[i-1][1])+a[i];
}
int ans=min(dp[n][1],dp[n][0]);
// cout<<ans<<' ';
dp[1][1]=1e18;
dp[1][0]=0;
rep(i,2,n){
dp[i][0]=dp[i-1][1];
dp[i][1]=min(dp[i-1][0],dp[i-1][1])+a[i];
}
ans=min(ans,dp[n][1]);
cout<<ans;
}
F
bfs dfs性质
给一张图,构造一个以1为根的生成树,要求所有非树边(原图中不在生成树中的边)都是/不是连接一个点和它在树里的祖先的。对这两种要求分别构造两棵树
其实就是dfs和bfs形成的两棵树。dfs会一直走一个分支,那么如果遇到了已访问过的点,这个以访问过的点一定是这个分支路径上的祖先,也就是返祖边。
bfs的非树边不会存在返祖边,假设存在,这相当于存在一个点,和两个深度不同的点同时有边,但是选择了深度较深的点作为树上的父亲,这在bfs里是不对的,那个深度较浅的点在跑bfs时会先遇到当前点,那么当前点在树里的父亲会是这个深度较浅的点。
实现时,跑dfs,bfs,如果遇到一个点时没访问过的,这条边就要选中,可以用map<pair>
记录一下,最后检查所有边,看有哪些边在map里
void solve() {
int n,m;
cin>>n>>m;
vvi g(n+1);
rep(i,1,m){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
vvi ans1,ans2;
vi vis0(n+1);
auto &&dfs=[&](auto &&dfs,int u,int f)->void{
vis0[u]=1;
for(int v:g[u]){
if(v==f||vis0[v])continue;
ans1.push_back({u,v});
dfs(dfs,v,u);
}
};
dfs(dfs,1,-1);
queue<int>q;
q.push(1);
vi vis(n+1);
vis[1]=1;
while(q.size()){
int u=q.front();
q.pop();
for(int v:g[u]){
if(!vis[v]){
vis[v]=1;
ans2.push_back({u,v});
q.push(v);
}
}
}
// cout<<"!\n";
for(auto &t:ans1){
cout<<t[0]<<' '<<t[1]<<'\n';
}
// cout<<"!\n";
for(auto &t:ans2){
cout<<t[0]<<' '<<t[1]<<'\n';
}
}