ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

福州市 2025 国庆集训 Day1 前三题题解

福州市 2025 国庆集训 Day1 前三题题解

福州市 2025 国庆集训 Day1 前三题题解

别问为啥只有前三题,因为后面我不会……

Day1 题单

T1 旅行

传送门

注意到 \(P\) 非常小,所以可以考虑指数级别的做法。

考虑状压 dp。设 \(f_{s,u}\) 表示经过 \(P\) 内的点集为 \(s\),当前位于 \(P\) 中第 \(u\) 号点的最短路径长度。

然而我们的路径其实是 \(1\rightarrow p_1\rightarrow \cdots \rightarrow p_n\rightarrow n\),所以其实点集的级别是 \(2^{p+2}\)

那么转移就是枚举 \(s\),然后枚举 \(s\) 内经过的点 \(i\),再枚举 \(s\) 内没经过的点 \(j\),得到新的状态 s'=s|(1<<j)

然后 \(f_{s',j}=\min \left\{f_{s,i}+dis_{p_i,p_j}\right\}\)。其中 \(dis_{p_i,p_j}\) 表示点 \(p_i\) 到点 \(p_j\) 的最短路径长度。可以用 Floyd 预处理出来。

最后的答案显然就是 \(f_{2^{p+2}-1,p+1}\)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
const int N=205,M=1e4+5,P=15;
const ljl inf=1e18;
int n,m,p,vec[N],cur=1;
ljl dis[N][N],ans,f[(1<<P)+5][N];
bool vis[N];
int main(){ios::sync_with_stdio(0);cin>>n>>m;for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(i!=j)dis[i][j]=inf;for(int i=1;i<=m;++i){ljl u,v,w;cin>>u>>v>>w;dis[u][v]=dis[v][u]=min(dis[u][v],w);}cin>>p;for(int i=1;i<=p;++i){cin>>vec[i];vis[vec[i]]=1;}vec[0]=1;vec[p+1]=n;for(int k=1;k<=n;++k)for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);memset(f,0x3f,sizeof(f));f[1][0]=0;for(int i=0;i<(1<<p+2);++i){for(int j=0;j<p+2;++j){if(!((i>>j)&1))continue;for(int k=0;k<p+2;++k){if((i>>k)&1)continue;f[i|(1<<k)][k]=min(f[i|(1<<k)][k],f[i][j]+dis[vec[j]][vec[k]]);}}}ans=f[(1<<p+2)-1][p+1];if(ans>=inf)cout<<-1<<'\n';else cout<<ans<<'\n';return 0;
}

T2 种植

传送门

先看看如果不考虑 \(q_i\),那么就是个简单的 01 背包板子。

但是它有 \(q_i\) 啊?

我们可以尝试下,发现把 \(q_i\) 按照从大到小的顺序排序后依次插入是最优的。

证明听了,但不会。/kel。只懂把某个时间段移到前面,与前面的交换,则前面的也一定可以对答案做出贡献。所以不会更劣

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=305,T=2e4+5;
int n,t,f[T],ans;
struct NODE{int p,q,r;bool operator < (const NODE x)const{return q>x.q;}
}node[N];
int main(){ios::sync_with_stdio(0);cin>>n>>t;for(int i=1;i<=n;++i)cin>>node[i].p>>node[i].q>>node[i].r;sort(node+1,node+n+1);for(int i=1;i<=n;++i){for(int j=t-node[i].q;j>=node[i].p;--j){f[j]=max(f[j],f[j-node[i].p]+node[i].r);}}for(int i=1;i<=t;++i)ans=max(ans,f[i]);cout<<ans<<'\n';return 0;
}

T3 最短路

题目传送门

妙妙题。

考虑额外加边的本质是什么。

通过 T1,不难想到其实就是把数字化作二进制,看成集合,\(i\) 可以向 \(j\) 连边当且仅当 \(j\)\(i\) 的真子集。

那么考虑怎么优化建图。

比如说当前数字为 \((101)_2\),那么可以 \((101)_2\rightarrow (100)_2\),以及 \((101)_2\rightarrow (001)_2\)。其中边权均为 \(0\)

那么就有问题了:正常来说边权是 \(1\) 啊?

其实这些点都是虚图。点编号是从 \(n+1\) 开始的。

所以我们就建好了图。

注意,这样的话,需要开的点的大小是 \(2\times 10^5+2^{20}\),边是 \(3\times 10^5+2^{20}\times 20\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+(1<<20)+5,M=3e5+(1<<20)*20+5;
int n,m,a[N],ehead[N],cnt_e,dis[N],maxa;
bool vis[N];
struct E{int to,w,pre;
}e[M];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > h;
void adde(int from,int to,int w)
{e[++cnt_e].to=to;e[cnt_e].w=w;e[cnt_e].pre=ehead[from];ehead[from]=cnt_e;return;
}
void dijk()
{memset(dis,0x3f,sizeof(dis));dis[1]=0;h.push(make_pair(dis[1],1));while(!h.empty()){int u=h.top().second;h.pop();if(vis[u])continue;vis[u]=1;for(int i=ehead[u];i;i=e[i].pre){int v=e[i].to,w=e[i].w;if(dis[v]>dis[u]+w){dis[v]=dis[u]+w;if(!vis[v])h.push(make_pair(dis[v],v));}}}return;
}
int main(){ios::sync_with_stdio(0);cin>>n>>m;for(int i=1;i<=n;++i)cin>>a[i];for(int i=1,u,v;i<=m;++i){cin>>u>>v;adde(u,v,1);}for(int i=1;i<=n;++i)adde(i,n+a[i]+1,0),adde(n+a[i]+1,i,1),maxa=max(maxa,a[i]);for(int i=1;i<=maxa;++i)//set i{//set i->set j(j\in i)for(int j=0;j<20;++j){if((i>>j)&1)adde(n+i+1,n+(i^(1<<j))+1,0);}}dijk();for(int i=1;i<=n;++i)cout<<(dis[i]==0x3f3f3f3f?-1:dis[i])<<'\n';return 0;
}
返回列表