码蹄集训练
BD202201 洞穴
https://www.matiji.net/exam/brushquestion/1/3956/4FCAF025E79704820690062E3FDE6CA1
分析:
从小到大依次处理边 这样的好处在于如果此时没有合适的中间点 我们只需合并就好 因为后续的边一定更大 不可能是中间点
其实发现就是联通块的合并
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
void solve();
void dfs(int);
const int maxn=105;
struct node {
int val,x,y;
bool friend operator<(node aa,node bb){
return aa.val>bb.val;
}
}a[maxn];
int n,cnt,fa[maxn];
priority_queue<node>Q;
int find(int x){
if(x!=fa[x])return fa[x]=find(fa[x]);
return x;
}
pair<int,int> res[maxn];
int main(){
int T;cin>>T;
while(T--)solve();
return 0;
}
void solve(){
cin>>n;
cnt=0;
for(int i=1;i<=n;i++)fa[i]=i;
for(int xx,i=1;i<=n;i++)
for(int j=1;j<=n;j++){
cin>>xx;
if(i<j){
node t;
t.x=i,t.y=j,t.val=xx;
Q.push(t);
}
}
while(!Q.empty()){
node t=Q.top();
Q.pop();
int fx=find(t.x),fy=find(t.y);
if(fx!=fy){
fa[fx]=fy;
res[++cnt]={t.x,t.y};
}
}
sort(res+1,res+1+cnt);
cout<<cnt<<endl;
for(int i=1;i<=cnt;i++)
cout<<res[i].first<<" "<<res[i].second<<endl;
}
BD202301 公园
分析:
真的太无语了 比赛的时候一开始就想到的做法 调代码调了两个小时十个点只过了八个 最后少写了一句话
if(dis1[i]!=maxx&&dis2[i]!=maxx&&dis3[i]!=maxx)
算是一道非常模板的题目把 两个起始点一定会相遇在某一点 然后再一起移动到终点
所以我们只要跑三次单源最短路 然后枚举相遇点就好了 比赛的时候枚举相遇点的时候没有特判能不能走到 寄了
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
#define int ll
void solve();
const int maxn=40005;
int w1,w2,w3,S1,S2,T,m,maxx;
ll dis1[maxn],dis2[maxn],dis3[maxn];
vector<int>G[maxn];
struct node{
int dis,to;
bool friend operator <(node aa,node bb){
return aa.dis>bb.dis;
}
}t;
priority_queue<node>Q;
signed main(){
int T;T=1;
while(T--)solve();
return 0;
}
void solve(){
cin>>w1>>w2>>w3;
cin>>S1>>S2>>T>>m;
memset(dis1,0x3f,sizeof(dis1));
memset(dis2,0x3f,sizeof(dis2));
memset(dis3,0x3f,sizeof(dis3));
maxx=dis1[0];
for(int i=1;i<=m;i++){
int xx,yy;
cin>>xx>>yy;
G[xx].push_back(yy);
G[yy].push_back(xx);
}
dis1[S1]=0;
t.dis=0,t.to=S1;
Q.push(t);
while(!Q.empty()){
int u=Q.top().to;
Q.pop();
for(int i=0;i<G[u].size();i++){
int to=G[u][i];
if(dis1[to]>dis1[u]+w1){
dis1[to]=dis1[u]+w1;
t.dis=dis1[to];
t.to=to;
Q.push(t);
}
}
}
dis2[S2]=0;
t.dis=0,t.to=S2;
Q.push(t);
while(!Q.empty()){
int u=Q.top().to;
Q.pop();
for(int i=0;i<G[u].size();i++){
int to=G[u][i];
if(dis2[to]>dis2[u]+w2){
dis2[to]=dis2[u]+w2;
t.dis=dis2[to];
t.to=to;
Q.push(t);
}
}
}
dis3[T]=0;
t.dis=0,t.to=T;
Q.push(t);
while(!Q.empty()){
int u=Q.top().to;
Q.pop();
for(int i=0;i<G[u].size();i++){
int to=G[u][i];
if(dis3[to]>dis3[u]+w1+w2-w3){
dis3[to]=dis3[u]+w1+w2-w3;
t.dis=dis3[to];
t.to=to;
Q.push(t);
}
}
}
if(dis1[T]==maxx||dis2[T]==maxx){
cout<<"-1"<<endl;
return;
}
int ans=1e18;
for(int i=1;i<=T;i++)
if(dis1[i]!=maxx&&dis2[i]!=maxx&&dis3[i]!=maxx)
ans=min(ans,dis1[i]+dis2[i]+dis3[i]);
cout<<ans<<endl;
}
BD202302蛋糕划分
分析:比赛的时候想到方法了 但是太难调了 细节太多了
如果枚举行的方案和列的方案肯定不行 爆复杂度 但是肯定是要枚举的
所以我们只枚举行的方案数 这个利用集合来枚举
首先我们二分最小答案 在行所有的情况下 如果有总的切刀数<=k的就是此答案成立
现在问题变成了 行的情况定下来了 要求列的切刀数尽量小 使之总和尽量<=k
对于列我们依次往后找最长能扩展的位置就好 同样也是二分
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
void solve();
int n,k,t,id,now,midd,S;
int a[20][20];
vector<int>Q;
int ask(int x1,int y1,int x2,int y2){
int res=a[x2][y2];
if(y1>=1)res-=a[x2][y1-1];
if(x1>=1)res-=a[x1-1][y2];
if(x1>=1&&y1>=1)res+=a[x1-1][y1-1];
return res;
}
bool ck(int l,int r){
int maxx=-1;
for(int i=1;i<Q.size();i++)
maxx=max(maxx,ask(Q[i-1]+1,l,Q[i],r));
if(maxx>midd)return false;
return true;
}
int find(int l){
int L,R,mid,res=-1;
L=l,R=n;
while(L<=R){
mid=L+R>>1;
if(ck(l,mid))
L=mid+1,res=mid;
else R=mid-1;
}
return res;
}
bool calc(){
bool pd=0;
for(int i=0;i<=S;i++){
t=i,id=1;
Q.clear();
Q.push_back(0);
while(t){
if(t&1)Q.push_back(id);
t>>=1;
id++;
}
Q.push_back(n);
now=1;
int tot=0,num=Q.size()-2;
if(k<num)continue;
while(1){
if(tot>k-num||now==n+1)break;
int R=find(now);
if(R==-1)break;
tot++;now=R+1;
}
if(now!=n+1)continue;
else {
pd=1;
break;
}
}
return pd;
}
void init(){
S=(1<<(n-1))-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]+=a[i][j-1];
for(int j=1;j<=n;j++)
for(int i=1;i<=n;i++)
a[i][j]+=a[i-1][j];
}
int main(){
int T;T=1;
while(T--)solve();
return 0;
}
void solve(){
cin>>n>>k;
init();
int LL,RR,ans;
LL=0,RR=1e9;
while(LL<=RR){
midd=(LL+RR)>>1;
if(calc())
RR=midd-1,ans=midd;
else LL=midd+1;
}
cout<<ans<<endl;
}
BD202317石碑文
分析:
考虑每一个前缀的贡献
每个前缀记录 当前有多少个shs 并且最后是s状态或者是sh状态或者是无效状态
考虑转移
s可以转移到无效和sh
sh可以转移到 shs个数不变的无效和shs个数加1的无效
无效可以转移到s和无效
记录答案就是对于每个前缀作为shs结尾的累加就行
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
void solve();
const int maxn=1e6+5;
const ll mod=1e9+7;
int n;
ll dp[5][5];//表示有多少个shs 0,1,2,最后的状态为无用状态0,sh 1,s 2
ll pre[5][5];//用于滚动
ll ans;
int main(){
int T;T=1;
while(T--)solve();
return 0;
}
void solve(){
cin>>n;
pre[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=2;j++){
if(j>=1)
dp[j][0]=(pre[j-1][1]+pre[j][0]*25+pre[j][1]*25+pre[j][2]*24)%mod;
else dp[j][0]=(pre[j][0]*25+pre[j][1]*25+pre[j][2]*24)%mod;
dp[j][1]=pre[j][2];
dp[j][2]=(pre[j][0]+pre[j][2])%mod;
}
ans=(ans*26+pre[2][1])%mod;
for(int j=0;j<=2;j++)
for(int z=0;z<=2;z++)
pre[j][z]=dp[j][z];
}
cout<<ans<<endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通