集训-2021四川省赛-ABDEHJKLM

A. Chuanpai

题意

每张牌上有两个数字x,y  (1≤x≤y≤6),两张牌不同当且仅当x1 != y1 或 x2 != y2.

有T(1≤T≤100)足测试数据.每组测试数据输入一个整数K (1≤K≤100),求有多少种不同的牌满足x+y=k。

思路:纯暴力

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
typedef long long LL;
int main()
{
	ios;
	int T;
	cin>>T;
	while(T--)
	{
		int k;
		cin>>k;
		int cnt=0;
		for(int x=1;x<=6;x++)
		{
			int y=k-x;
			if(y<1||y>6||y<x) continue;
			cnt++;
		}
		cout<<cnt<<endl;
	}
	return 0;
}

B. Hotpot

题意

编号0∼(n−1)的n个人依次围在火锅旁,每个人有一个喜悦值,初始时为0.锅中共有k种原料,其中第i  (0≤i≤n−1)个人喜欢原料ai,初始时锅中无原料.从0号人开始,每个人轮流做m次操作,轮到第i个人时,若锅中有他喜欢的原料,他会吃掉这个原料并增加1点快乐值;否则他会加入一个他喜欢的原料ai.求最终每个人的快乐值.

有T (1≤T≤1000)组测试数据.每组测试数据第一行输入三个整数n,k,m (1≤n,k≤1e5,1≤m≤1e9).第二行输入n个整数a0,⋯,an−1  (1≤ai≤k).数据保证所有测试数据的n之和、k之和都不超过2e5.

思路: 这题一看m范围那么大,就知道暴力模拟是没戏的,而n和k的数据范围比较小,这是一个突破点,我们先随便考虑一种原料的情况(假设考虑原料1)

我们先做m/(2n)次周期,再做m%(2n)次暴力模拟。

代码如下: 

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
typedef long long LL;
const int N=100010;
bool st[N];
int h[N],a[N];
vector<int>v[N];
int main()
{
	ios;
	int T;
	cin>>T;
	while(T--)
	{
		int n,m,k;
		cin>>n>>k>>m;
		for(int i=1;i<=k;i++)
			st[i]=false,v[i].clear();
		for(int i=0;i<n;i++)
			h[i]=0;
		for(int i=0;i<n;i++)
			cin>>a[i],v[a[i]].push_back(i);
		int x=m/(2*n);
		for(int i=1;i<=k;i++)
			if(v[i].size()%2==0)
			{
				for(int j=1;j<v[i].size();j+=2)
					h[v[i][j]]+=2*x;
			}else
			{
				for(int j=0;j<v[i].size();j++)
					h[v[i][j]]+=x;
			}
		int t=m%(2*n);
		for(int i=0;i<t;i++)
		{
			int code=i%n;
			if(st[a[code]])
			{
				st[a[code]]=false;
				h[code]++;
			}else st[a[code]]=true;
		}
		for(int i=0;i<n;i++)
			if(i==0) cout<<h[i];
			else cout<<" "<<h[i];
		cout<<endl;
	}
	return 0;
}

 

D. Rock Paper Scissors

题意

A和B玩游戏.有三种类型的牌:石头、布、剪刀.初始时每个玩家有n张牌,游戏有n轮,A先打出一张牌,展示给B,然后B再打出一张牌.打出的牌不能再回收.最后总得分等于每轮得分相加.A赢了-1分,B赢了+1分,A想最小化得分,B想最大化得分。

有T  (1≤T≤1000)组测试数据.每组测试数据第一行输入三个整数br,bp,bs分别表示A手中的石头、布、剪刀牌数.第二行输入三个整数dr,dp,ds  (0≤br,bp,bs,dr,dp,ds≤1e9),分别表示B手中的石头、布、剪刀牌数.数据保证br+bp+bs=dr+dp+ds.

思路:由于A先出牌并且B会知道A出的是什么牌,那么A的出牌顺序其实一点都不重要,A出石头的话,B一定会出布,没有布就出石头,石头也没有了就出剪刀,我们用0表示石头,1表示布,2表示剪刀。如果这样表示,就使代码很简单了,假设A出i,B先考虑出能赢A的牌(i+1)%3,再考虑跟A出一样的i,然后再考虑出输给A的牌(i-1+3)%3.

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
typedef long long LL;
int a[3],b[3];
int main()
{
	ios;
	int T;
	cin>>T;
	while(T--)
	{
		for(int i=0;i<3;i++)
			cin>>a[i];
		for(int i=0;i<3;i++)
			cin>>b[i];
		LL res=0;
		for(int i=0;i<3;i++)
		{
			int x=(i+1)%3;
			int minv=min(a[i],b[x]);
			res+=minv,a[i]-=minv,b[x]-=minv;
			x=i;
			minv=min(a[i],b[x]);
			a[i]-=minv,b[x]-=minv;
			x=(i+2)%3;
			minv=min(a[i],b[x]);
			res-=minv,a[i]-=minv,b[x]-=minv;
		}
		cout<<res<<endl;
	}
	return 0;
}

E. Don’t Really Like How The Story Ends

题意

给定一张包含n个节点、m条边的无向图,问至少添加多少条边才能使得从节点1开始的DFS序为1∼n.

有T组测试数据.每组测试数据第一行输入两个整数n,m  (1≤n,m≤1e5).接下来�行每行输入两个整数u,v (1≤u,v≤n),表示节点u与v间存在边.数据保证所有测试数据的(n+m)之和不超过1e6.

看的其他大佬的解题思路

思路:

从节点1开始搜索,若节点1与节点2间存在边,则继续搜索节点2.

①若节点2与节点3间存在边,继续搜索节点3.

②若节点2与节点3间不存在边,且节点2不与节点u  (u>3)相连,则节点2已无搜索价值,直接回溯即可.

③若节点2与节点3间不存在边,但节点2与节点u  (u>3)相连,则需添加一条边<2,3>.

DFS模拟上述过程即可.

我们在求解的时候对于当前结点,我们遍历其所连的其他结点时,是要按照从小到大的顺序来的,因此本题建边用vector容器,要记得排序。

还有就是要注意连一条节点1到节点(n+1)的边作为哨兵,主要是为了dfs函数中while(x>=vis)能够发挥作用,否则若1是孤立点则无法往下搜.

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
typedef long long LL;
const int N=100010,M=2*N,INF=0x3f3f3f3f;
bool st[N];
vector<int>v[N];
int n,m,res,vis;//vis是接下来需要访问的结点编号
void dfs(int u)  //u是当前结点的编号
{
	if(u==n+1) return;
	for(int i=0;i<v[u].size();i++)
	{
		int x=v[u][i];
		if(x<vis) continue;
		while(x>=vis)
		{
			if(x==vis) dfs(vis++);
			else 
			{
				res++,vis++;
				dfs(vis-1);
			}
		}
	}
}
int main()
{
	ios;
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		for(int i=1;i<=n;i++)
			v[i].clear(),st[i]=false;
		res=0;
		while(m--)
		{
			int a,b;
			cin>>a>>b;
			if(a==b) continue;
			if(a>b) swap(a,b);
			v[a].push_back(b);
		}
		v[1].push_back(n+1);
		for(int i=1;i<=n;i++)
			sort(v[i].begin(),v[i].end());
		vis=2;
		dfs(1);
		cout<<res<<endl;
	}
	return 0;
}

 

H. Nihongo wa Muzukashii Desu

思路: 纯模拟,这个应该都会,题目太长也就不贴了,代码如下。

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
typedef long long LL;
const int N=100010;
string str[]={"tte","nde","ite","ide","shite"};
int main()
{
	ios;
	int T;
	cin>>T;
	while(T--)
	{
		string s;
		cin>>s;
		int n=s.size();
		if(s[n-6]=='m'||s[n-6]=='b'||s[n-6]=='n') s.replace(n-6,6,str[1]);
		else if(s[n-6]=='r') s.replace(n-6,6,str[0]);
		else if(s[n-6]=='k')
		{
			if(s=="ikimasu") s="itte";
			else s.replace(n-6,6,str[2]);
		}
		else if(s[n-6]=='g') s.replace(n-6,6,str[3]);
		else if(s[n-6]=='h') 
		{
			if(n>=7)
			{
				if(s[n-7]=='c') s.replace(n-7,7,str[0]);
				else if(s[n-7]=='s') s.replace(n-7,7,str[4]);
			}
		}
		cout<<s<<endl;
	}
	return 0;
}

 

J. Ants

题意

编号1∼n的n只蚂蚁在长为(1e9+1)的木棒上,初始时第i  (1≤i≤n)只蚂蚁在距离棒左端ai单位处,有些蚂蚁面朝左,有些蚂蚁面朝右.所有蚂蚁以速度1单位/s运动,两蚂蚁相遇时各自立即转向.棒的两端各有一个障碍物,每个障碍物有一个耐久值,左边的耐久值为A,右边的耐久值为B,耐久值降为0时障碍物被破坏.一只蚂蚁撞到障碍物时会立即转身,同时障碍物的耐久−1.经过被破坏的障碍物的蚂蚁会掉下木棒.求所有蚂蚁掉下木棒所需的时间.

第一行输入三个整数n,a,b  (1≤n≤1e6,1≤a,b≤1e9).第二行输入n格整数a1,⋯,an  (1≤ai≤1e9,ai<ai+1).第三行输入n个整数d1,⋯,dn  (di∈{0,1}),其中di=0表示第i只蚂蚁初始时面朝左,di=1表示第i只蚂蚁初始时面朝右.

思路:

两只蚂蚁相撞的情况我们可以看成是两只蚂蚁互相穿过了对方,因此我们可以忽视蚂蚁相撞。这题同样的A,B的值太大了,不能暴力模拟。木棒长度用len表示(len=1e9+1)

我们考虑周期性,2*len为一个周期,每经过2*len秒,所有的蚂蚁都会回到初始状态,并且A,B的值都会减少n,因此我们可以先做min(a,b)/n个周期,再进行模拟,用两个小根堆left,right分别表示面朝左、右的蚂蚁走到对应的端点所需的时间,每次取所需时间最短的蚂蚁进行扩展,转换方向后插入另一个堆.

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
typedef long long LL;
const int N=1000010,M=2*N,INF=0x3f3f3f3f;
LL pos[N],d[N];
LL len=1e9+1;
int main()
{
	ios;
	LL n,a,b;
	cin>>n>>a>>b;
	for(LL i=1;i<=n;i++)
		cin>>pos[i];
	for(LL i=1;i<=n;i++)
		cin>>d[i];
	LL k=min(a,b)/n;
	a-=k*n,b-=k*n;
	LL res=(LL)k*2*len;
	LL maxv=0;//maxv记录了在剩余不完整周期内,蚂蚁全部掉下去需要的时间
	priority_queue<LL,vector<LL>,greater<LL> >left,right;
	for(LL i=1;i<=n;i++)
	{
		if(d[i]==0) left.push(pos[i]);
		else right.push(len-pos[i]);
	}
	while(left.size()||right.size())
	{
		if(left.size())
		{
			LL t=left.top();
			left.pop();
			maxv=max(maxv,t);
			if(a)
			{
				a--;
				right.push(t+len);  //蚂蚁转向后要想掉下去必须在原来的基础上加上len
			}
		}
		if(right.size())
		{
			LL t=right.top();
			right.pop();
			maxv=max(maxv,t);
			if(b)
			{
				b--;
				left.push(t+len);
			}
		}
	}
	res+=maxv;
	cout<<res<<endl;
	return 0;
}

K-skip Permutation

题意

对1∼n的一个排列P=p1p2⋯pn,定义f(P,K)为使得pi+k=pi+1的i∈[1,n)的个数.给定整数n,k  (1≤n,k≤1e6),构造一个1∼n的排列使得f(P,K)最大.

思路:

1,1+k,1+2k,……,2,2+k,2+2k……这样构造一定会是最优的

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
const int N=1000010;
bool st[N];
int main()
{
	ios;
	int n,k;
	cin>>n>>k;
	bool f=true;
	for(int i=1;i<=n;i++)
	{
		if(st[i]) continue;
		int x=i;
		while(x<=n)
		{
			if(f) cout<<x,f=false;
			else cout<<" "<<x;
			st[x]=true;
			x+=k;
		}
	}
	return 0;
}

 

L. Spicy Restaurant

题意

有编号1∼n的n个餐厅,其中第i  (1≤i≤n)个餐厅的火锅辣度为wi.每个餐厅可视为包含m条边的无向图上的一个节点,每条边的权值都为1.现有q个人,每个人有一个能接受的最大辣度.对每个人,求他到一个他能接受的辣度的餐厅的最短距离.

第一行输入三个整数n,m,q  (1≤n,m≤1e5,1≤q≤5e5).第二行输入n个整数w1,⋯,wn  (1≤wi≤100).接下来m行每行输入两个相异的整数u,v  (1≤u,v≤n),表示节点u与v间存在边.接下来q行每行输入两个整数p,a  (1≤p≤n,1≤p≤a),分别表示一个人所在的节点编号和他能接受的最大辣度.

对每个人,输出其到他能接受的辣度的餐厅的最短距离,若不存在这样的餐厅,输出−1.

 思路:

这题一看n,m,q都那么大,那肯定是不能暴力跑最短路求解的,突破点在w,我们可以看到w的范围只有100,如果我们把所有的w都给枚举一遍,最多也只需要100次,那么我们可以考虑求某个人x到辣度为y的餐厅的最短距离,如果枚举人的话似乎不是很好求,我们把思维转化一下,我们改为枚举辣度,以所有辣度为y的餐厅为起点,求到其他点的最短距离,这就是一个有多个起点的BFS问题,我们可以建立超级源点,也可以把所有辣度为y的结点初始化距离为0.

这题我们需要预处理出来一个数组dist[i][j]:辣度为i的餐厅到餐厅j的最短距离,然后对于每个询问枚举辣度值即可。

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
typedef long long LL;
const int N=100010,M=2*N,INF=0x3f3f3f3f;
int h[N],e[M],ne[M],idx;
int n,m,q;
int w[N];
int dist[110][N];
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs(int spicy,int dist[])
{
	queue<int>q;
	for(int i=1;i<=n;i++)
		if(w[i]==spicy) q.push(i),dist[i]=0;
	while(q.size())
	{
		int t=q.front();
		q.pop();
		for(int i=h[t];~i;i=ne[i])
		{
			int j=e[i];
			if(dist[j]>dist[t]+1)
			{
				dist[j]=dist[t]+1;
				q.push(j);
			}
		}
	}
}
int main()
{
	ios;
	memset(h,-1,sizeof h);
	memset(dist,0x3f,sizeof dist);
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++)
		cin>>w[i];
	while(m--)
	{
		int a,b;
		cin>>a>>b;
		add(a,b),add(b,a);
	}
	for(int i=1;i<=100;i++)
		bfs(i,dist[i]);
	while(q--)
	{
		int p,a;
		cin>>p>>a;
		int res=INF;
		for(int i=1;i<=a;i++)
			res=min(res,dist[i][p]);
		if(res==INF) res=-1;
		cout<<res<<endl;
	}
	return 0;
}

M. True Story

题意

编号1∼n的n个人要赶不上飞机了.在0时初,他们距机场x km,登机时间为p0时初.第i  (1≤i≤n)个人的速度为si km/h,他们在不晚于登机时间赶到机场.现登机时间会推迟k次,其中第i  (1≤i≤k)次推迟会在ti时初发布,并将登机时间推迟到pi时初.每次推迟发布时每个人会立即收到消息,且每个人只会在推迟发布时才能知道消息,不能提前预判.每个人只会在自己能赶上飞机的前提下才会移动,若他发现此时已赶不上飞机,他会停在原地;否则他会从上次停下的地方继续移动.求最后能赶上飞机的人数.

思路:

刚开始的时候,每个人有p0的时间赶飞机,如果通过计算发现在这p0的时间内能赶上飞机,那他就会行动,否则还是待在原地摆烂,直到发布推迟消息,计算p[i]-t[i]这段时间自己能不能赶上飞机,如果能,就可以顺利上飞机,如果不能,还是留在原地。

说白了这题只要考虑在最大间隔max(p0,max(p[i]-t[i]))的时间内能不能赶上飞机即可

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
typedef long long LL;
const int N=100010;
int s[N],t[N],p[N];
int main()
{
	ios;
	int n,k,x,p0;
	cin>>n>>k>>x>>p0;
	for(int i=1;i<=n;i++)
		cin>>s[i];
	for(int i=1;i<=k;i++)
		cin>>t[i];
	for(int i=1;i<=k;i++)
		cin>>p[i];
	int cnt=0;
	int maxv=p0;
	for(int i=1;i<=k;i++)
		maxv=max(maxv,p[i]-t[i]);
	for(int i=1;i<=n;i++)
		if(x<=(LL)maxv*s[i]) cnt++;
	cout<<cnt<<endl;
	return 0;
}

 

  • 28
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值