码蹄集训练

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; 
}

posted @   wzx_believer  阅读(306)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示