牛客小白月赛22补题

牛客小白月赛22比赛界面
小白月赛22题解

难度体验

签到:E.模拟、F.思维
简单:D.爆搜、G.暴力、J.大数模板
中级:A.STL、B.树形DP
困难:C.记忆化搜索、H.差分
压轴:I.计算几何

A.操作序列

题意

给出一个长度无限的数列,初始全部为零,有三种操作:

  • 增加操作:给下标为 t t 的数加 c c 。特别注意,如果在下标 [ t 30 , t + 30 ] [t-30,t+30]
    内有不为零的数,增加操作无效。
  • 削减操作:让数列中下标最小的不为零数变为零。
  • 查询操作:查询数列中下标为 t t 的数字是多少。

思路

STL模拟,用map维护实际数组,set维护非0数组的下标。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007;
map<int,int>mp;
set<int>st;//维护范围内非0数
map<int,int>::iterator it;
set<int>::iterator it2;
void add(int x,int c)
{
	bool flag=1;
	it2=st.lower_bound(x-30);
	for(set<int>::iterator it3=it2;it3!=st.end();it3++)
	{
		if(*it3<=x+30)
			return;
		else
			break;
	}
	if(mp[x]+c==0)
	{
		mp.erase(x);
		st.erase(x);
	}
	st.insert(x);
	mp[x]+=c;
}
int sub()
{
	if(st.empty())
		return 0;
	it=mp.begin();
	int ret=it->second;
	st.erase(st.begin());
	mp.erase(mp.begin());
	return ret;
}
int query(int x)
{
	it2=st.find(x);
	if(it2==st.end())//不在
		return 0;
	return mp[x];
}
int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	#ifdef DEBUG
		freopen("input.in", "r", stdin);
	//	freopen("output.out", "w", stdout);
	#endif
	int n,t,c;
	scanf("%d",&n);
	while(n--)
	{
		char ch='?';
		scanf("%d%c",&t,&ch);
		if(ch==' ')
		{
			scanf("%d",&c);
			add(t,c);//a[t]+=c
			continue;
		}
		if(t==-1)
		{
			if(st.empty())
				printf("skipped\n");
			else{
				printf("%d\n",sub());
			}
		}
		else
			printf("%d\n",query(t));
	}
	return 0;
}

B.树上子链

题意

给定一棵树 T ,树 T 上每个点都有一个权值。
定义一颗树的子链的大小为:这个子链上所有结点的权值和 。
请在树 T 中找出一条最大的子链并输出。

思路

前缀和,树形DP。
通过DFS维护根节点到节点x的前缀和,以及x节点到叶子结点的最大值与次大值。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007;
vector<int>G[maxn];
ll val[maxn],sum[maxn],dp[maxn],dp2[maxn],ans;
void dfs(int x,int fa)
{
	sum[x]=sum[fa]+val[x];
	dp[x]=val[x];
	dp2[x]=-inf;
	for(auto &v:G[x])
	{
		if(v==fa)
			continue;
		dfs(v,x);
		if(dp[v]+val[x]>dp[x])
		{
			dp2[x]=dp[x];
			dp[x]=dp[v]+val[x];
		}
		else
			dp2[x]=max(dp2[x],dp[v]+val[x]);
	}
	ans=max(ans,max(sum[fa]+dp[x],dp[x]+dp2[x]-val[x]));
}
int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	#ifdef DEBUG
		freopen("input.in", "r", stdin);
	//	freopen("output.out", "w", stdout);
	#endif
	int n,u,v;
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>val[i];
	for(int i=1;i<n;i++)
	{
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	ans=val[1];
	dfs(1,0);
	cout<<ans<<endl;
	return 0;
}

C.交换游戏

题意

有12个孔,有些孔上面有障碍。
若如果相邻的三个孔有两个孔被遮挡,并且被遮挡的两个孔相邻,则可以将中间的障碍拿掉,并将一端的遮挡物移到另一端没有被遮挡的孔上面。
简单来说:将这三个孔的状态反转
例如记x为障碍遮住的孔,o为未遮住的孔
oxx -> xooxxo -> oox

思路

记忆化搜索
将状态看作二进制,枚举计算每一种组合的答案, O ( 1 ) O(1) 查询。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007;
bool vis[1<<13];
int ans[1<<13];
void dfs(int x)
{
	if(vis[x])
		return;
	vis[x]=1;
	int cnt=0;
	for(int i=1;i<=(1<<11);i<<=1)
		if(x&i)
			cnt++;
	ans[x]=cnt;
	for(int i=3,t1=1,t2=2,t3=4;i<=12;i++,t1<<=1,t2<<=1,t3<<=1)
	{//分别为这三个位的状态
		if((x&t2)&&!((x&t1)&&(x&t3))&&((x&t1)||(x&t3)))
		{
			if(x&t1)
			{
				dfs(x-t2-t1+t3);
				ans[x]=min(ans[x],ans[x-t2-t1+t3]);
			}
			else{
				dfs(x-t2-t3+t1);
				ans[x]=min(ans[x],ans[x-t2-t3+t1]);
			}
		}
	}
}
int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	#ifdef DEBUG
		freopen("input.in", "r", stdin);
	//	freopen("output.out", "w", stdout);
	#endif
	for(int i=0;i<(1<<12);i++)//预处理
		dfs(i);
	int t;
	string s;
	cin>>t;
	while(t--)
	{
		cin>>s;
		int now=0;
		for(auto &ch:s)
		{
			now<<=1;
			if(ch=='o')
				now|=1;
		}
		cout<<ans[now]<<endl;
	}
	return 0;
}

D.收集纸片

题意

我们把房间按照笛卡尔坐标系进行建模之后,每个点就有了一个坐标。
假设现在房子里有些纸片需要被收集,收集完纸片你还要回归到原来的位置,你需要制定一个策略来使得自己行走的距离最短。
你只能沿着 x 轴或 y 轴方向移动:从位置 (i,j) 移动到相邻位置 (i+1,j),(i-1,j),(i,j+1) 或 (i,j-1) 距离增加 1。

思路

范围不大,爆搜即可

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007;
struct node
{
	int x,y;
} a[15];
int n,sx,sy,ans;
int dis(int x1,int y1,int x2,int y2)
{
	return abs(x1-x2)+abs(y1-y2);
}
int dis(int s,int t)
{
	if(s==0)//s是起点
		return abs(sx-a[t].x)+abs(sy-a[t].y);
	return abs(a[s].x-a[t].x)+abs(a[s].y-a[t].y);
}
bool vis[maxn];
//queue<int>q;
void dfs(int t,int tim,int tot)
{
	if(tim==n)
	{
		ans=min(ans,tot+dis(sx,sy,a[t].x,a[t].y));
		return;
	}
	for(int i=1;i<=n;i++)
	{
		if(!vis[i])
		{
			vis[i]=1;
			dfs(i,tim+1,tot+dis(t,i));
			vis[i]=0;
		}
	}
}
int main()
{
	int t,r,c;
	cin>>t;
	while(t--)
	{
		cin>>r>>c>>sx>>sy>>n;
		for(int i=1;i<=n;i++)
			cin>>a[i].x>>a[i].y;
		ans=inf;
		dfs(0,0,0);
		cout<<"The shortest path has length "<<ans<<endl;
	}
	return 0;
}

E. 方块涂色

签到题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
	ll n,m,r,c;
	while(cin>>n>>m>>r>>c)
	{
		cout<<(n-r)*(m-c)<<endl;
	}
	return 0;
}

F. 累乘数字

签到题,有点小思维

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007;
int main()
{
	string n;
	ll d;
	while(cin>>n>>d)
	{
		cout<<n;
		while(d--)
			cout<<"00";
		cout<<endl;
	}
	return 0;
}

G.仓库选址

思路

比赛时没开这题…… O ( n 4 ) O(n^4) 无脑暴力做

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=105,inf=0x3f3f3f3f,mod=1000000007;
int mp[maxn][maxn];
int dis(int x1,int y1,int x2,int y2)
{
	return abs(x1-x2)+abs(y1-y2);
}
int main()
{
	int t,n,m;
	cin>>t;
	while(t--)
	{
		cin>>m>>n;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				cin>>mp[i][j];
		int ans=inf;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				int now=0;
				for(int k=1;k<=n;k++)
					for(int l=1;l<=m;l++)
						now+=mp[k][l]*dis(i,j,k,l);
				ans=min(ans,now);
			}
		cout<<ans<<endl;
	}
	return 0;
}

H.货物种类

比赛时看了题目想了树套树的假做法,觉得空间复杂度炸裂+难写就没继续想……

题意

n个仓库,编号从1到n。
当购进某种货物的时候,商家会把货物分散的放在编号相邻的几个仓库中。
当所有货物购买完毕,存放货物种类最多的仓库编号为多少?

思路

离散化,差分,看代码

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007;
struct ope
{
	int l,r,g;
	ope(int l,int r,int g):
		l(l),r(r),g(g){}
};
vector<int>des,be[maxn],ed[maxn];
int res[maxn];
int main()
{
	vector<ope>op;
	int n,m,l,r,d,cnt=0;
	cin>>n>>m;
	while(m--)
	{
		cin>>l>>r>>d;//[l,r]存入货物d
		op.push_back(ope(l,r,d));
		des.push_back(d);
	}
	sort(des.begin(),des.end());//对货物去重
	des.erase(unique(des.begin(),des.end()),des.end());
	for(auto &i:op)
	{
		i.g=lower_bound(des.begin(),des.end(),i.g)-des.begin()+1;
		be[i.l].push_back(i.g);
		ed[i.r].push_back(i.g);
	}
	int ans=0,num=0,now=0;
	for(int i=1;i<=n;i++)
	{//now维护i的货物种类数
		for(int j:be[i])//[i,?]范围的货物j
		{
			if(!res[j])//该区域初次被j覆盖
				now++;
			res[j]++;//res是一个差分数组
		}
		if(now>ans)
		{
			ans=now;
			num=i;//种类最多的货舱
		}
		for(int j:ed[i])//[?,i]范围的货物
		{
			res[j]--;
			if(!res[j])//该区域不再被j覆盖
				now--;
		}
	}
	cout<<num<<endl;
	return 0;
}

J.计算A+B

题目没啥好说的,一个裸的大数板子

思路

大数加法

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007;
//#define lowbit(x) ((x) & -(x))//<<setiosflags(ios::fixed)<<setprecision(9)
void read(){}
template<typename T,typename... T2>inline void read(T &x,T2 &... oth) {
	x=0; int ch=getchar(),f=0;
	while(ch<'0'||ch>'9'){if (ch=='-') f=1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	if(f)x=-x;
	read(oth...);
}
const int Length=10005;
string add(const string &a,const string &b)//加
{
    string ans;
    int na[Length]={0},nb[Length]={0};
    int la=a.size(),lb=b.size();
    for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
    for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    for(int i=0;i<lmax;i++) na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10;
    if(na[lmax]) lmax++;
    for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
    return ans;
}
int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	#ifdef DEBUG
		freopen("input.in", "r", stdin);
	//	freopen("output.out", "w", stdout);
	#endif
	int n;
	string s;
	cin>>n;
	while(n--)
	{
		cin>>s;
		string a,b;
		bool flag=0;
		for(auto &ch:s)
		{
			if(ch=='+')
			{
				flag=1;
				continue;
			}
			if(!flag)
				a+=ch;
			else
				b+=ch;
		}
		if(a.empty()||b.empty())
			cout<<"skipped"<<endl;
		else{
			cout<<add(a,b)<<endl;
		}
	}
	return 0;
}
发布了71 篇原创文章 · 获赞 12 · 访问量 7436

猜你喜欢

转载自blog.csdn.net/Miaplacidus/article/details/104460360