暑假训练round 3 题解

32 篇文章 0 订阅
29 篇文章 0 订阅

今天做题运气出奇的好,除了几处小错误调试之后忘记改掉了……最后还AK了……虽然题目不难,学长也说是福利局,但是对个人的鼓励作用还是挺大的……至此暑假训练就结束了,也算没有遗憾……。

题解如下:

Problem A: 苦逼的MCA


Time Limits:  1000 MS   Memory Limits:  65536 KB

64-bit interger IO format:  %lld   Java class name:  Main


Description

在TUBN这个地方,有个苦逼的人叫MCA,迫于生计,在这个大热天的夏天买起了西瓜。他有n个西瓜,每个西瓜价格为ai元。规定每个客人最多只能买一个西瓜,而且会挑最便宜的西瓜。
若客人来的多了,超过了西瓜的个数,那就不够了,那MCA只能苦逼地支付不能买到西瓜的客人每人d元。问苦逼的MCA最大的收益是多少(可能为负值)。

Input

有多组数据,每组数据第一行输入三个数n和d还有m(1 <= n, d, m <= 100),分别表示西瓜个数,付给不能买瓜的客人的钱,客人的数量。
接下来一行包含n个数,a[1]...a[n](1 <= a[i] <= 100),表示每个西瓜的价格。

Output

对于每组测试,输出一行答案,代表最大收益。

Sample Input

2 1 2
2 1
2 1 10
2 1

Output for Sample Input

3
-5

一看就感觉是非常久远的CF上的某一道题,当时好像题面是卖冰棍……而且是求最后的收益,一个优先队列就搞定的水题

代码:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define MM(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=1010;
int main(void)
{
	int n,d,m,i,j;
	while (~scanf("%d%d%d",&n,&d,&m))
	{
		priority_queue<int,vector<int>,greater<int> >Q;
		for (i=0; i<n; ++i)
		{
			int v;
			scanf("%d",&v);
			Q.push(v);
		}
		int res=0,ans=0;;
		while (!Q.empty()&&m)
		{
			int now=Q.top();
			Q.pop();
			res+=now;
			ans=max(ans,res);
			m--;
		}
		if(m)
		{
			ans-=m*d;
		}
		printf("%d\n",ans);
	}
	return 0;
}

Problem B: Hwl的水题之二


Time Limits:  1000 MS   Memory Limits:  65536 KB

64-bit interger IO format:  %lld   Java class name:  Main


Description

Hwl大神喜欢位运算,尤其是异或运算。现在有n个数,下标为1到n.

现在有q个询问,问Hwl大神xor值为k的数对有多少种(例如下标为1 3的数对和下标为3 1的数对算一种;下标为1 1不算数对,即两个数下标不能相同)
 

Input

有多组测试数据,每组数据输入两个数n(2 <= n <= 100000)和q(1 <= q <= 10000),分别表示数字的数量和询问个数。
接下来一行输入n个数,a[i]表示数的大小(1 <= i <= n , 0 <= a[i] <= 1000)。
接下来q行,每一行输入一个数k 表示问你有多少种数对xor为k。(0 <= k <= 5000)。

Output

对于每一个询问输出对应的答案。

Sample Input

4 3
0 1 1 2
1
0
9

Output for Sample Input

2
1
0

Hint

例子中当k为1时,数对有(a[1], a[2])和(a[1], a[3])


这题思想比较猥琐,想了好一会儿……刚开始想着用二分,本地调试一会儿过了样例,交上去超时(当时感觉难道没有比这更快的做法了),然后想想昨天做的一道题,求的是两两之间的和为n有几对,但是看了范围显然那个方法不适用于此题,然后发现a[i]比较小只有1000,估计切入点就在这里,然后感觉应该就是暴力100W数据预处理,然后O(1)回答

代码:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define MM(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=100010;
int pos[N];
LL cnt[1010];
LL ck[5010];
int main(void)
{
	int n,k,m;
	LL i,j;
	while (~scanf("%d%d",&n,&m))
	{
		MM(cnt,0);
		MM(ck,0);
		for (i=0; i<n; ++i)
		{
			scanf("%d",&pos[i]);
			++cnt[pos[i]];
		}
		LL now;
		for (i=0; i<=1000; ++i)
		{
			for (j=i; j<=1000; ++j)
			{
				now=i^j;
				if(i==j)
					ck[now]+=(cnt[i]*(cnt[i]-1LL)/2LL);
				else
					ck[now]+=(cnt[i]*cnt[j]);
			}
		}
		for (i=0; i<m; ++i)
		{
			cin>>k;
			cout<<ck[k]<<endl;
		}
	}
	return 0;
}

Problem C: GL or BL


Time Limits:  1000 MS   Memory Limits:  65536 KB

64-bit interger IO format:  %lld   Java class name:  Main


Description

有一场聚会,n个人,有男有女。现有m对喜欢的关系。问你在这场聚会是不是一定有同性相爱(Girl Love or Boy Love)。

Input

有多组测试数据,每组数据输入n和m(1 < n <= 100 , 0 <= m <= n*n),分别表示人数和关系对数。
接下来有m行,每行两个数u和v(1 <= u, v <= n && u != v),分别表示编号为u和v的人互相喜欢。

Output

要是这场聚会中,要是一定有同性相爱,就输出"Yes",要是不一定,就输出"No"。

Sample Input

2 2
1 2
2 1

3 3
1 2
2 3
3 1

Output for Sample Input

No
Yes

Hint

注意可能有重复的关系,比如样例一
第一个样例
1若是男的,则2可以是女的
1若是女的,则2可以是男的
所以是No
第二个样例
1若是男的,则2是女的,则3也是女的
2和3性别相同,所以是Yes


一开始以为是简单的跑个最大匹配即可,然而样例都过不了,后来发现并不是这样,应该是判断是否不出现非二分图,注意红字的意思,一旦出现一个非二分图就说明染色遇到了同样颜色,而颜色是代表性别的,那也就是说存在同性相爱也就是说对于每一个空的点都进行BFS判断二分图,一旦出现非二分图(同性相爱情况)就break,最后输出结果,记得要用双向边,如果是单向边,那么这组数据1-2,2-3,4-3会出现同性相爱,但画个图就知道并不会这样,原因就是没有一次性把入队的点所具有的关系全部拓展,可以类比想想相爱那肯定是双向的啊,所以当然是双向边了

另外此题用种类并查集也可以做,再贴一份代码

BFS染色代码:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define MM(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=9010;
struct info
{
	int to;
	int pre;
}E[N];
int head[N],cnt;
int color[N];
int vis[N];
void add(int s,int t)
{
	E[cnt].to=t;
	E[cnt].pre=head[s];
	head[s]=cnt++;
}
void init()
{
	MM(head,-1);
	cnt=0;
	MM(color,0);
	MM(vis,0);
}
bool bfs(int now)
{
	queue<int>Q;
	color[now]=1;
	Q.push(now);
	while (!Q.empty())
	{
		int now=Q.front();
		Q.pop();
		for (int i=head[now]; ~i; i=E[i].pre)
		{
			int v=E[i].to;
			if(!color[v])
			{
				color[v]=(color[now]==1?2:1);
				Q.push(v);
			}
			else if(color[v]==color[now])
				return false;
		}
	}
	return true;
}

int main(void)
{
	int n,m,i,j,a,b,c;
	while (~scanf("%d%d",&n,&m))
	{
		init();
		for (i=0; i<m; ++i)
		{
			scanf("%d%d",&a,&b);
			vis[a]=vis[b]=1;
			add(a,b);
			add(b,a);
		}
		int flag=1;
		for (i=1; i<=n; ++i)
		{
			if(vis[i]&&!color[i])
			{
				if(!bfs(i))
				{
					flag=0;
					break;
				}	
			}
		}
		puts(flag?"No":"Yes");
	}
	return 0;
}

种类并查集代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define CLR(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=110;
struct info
{
	int pre;
	int rela;
};
info node[N];
void init()
{
	for (int i=0; i<N; ++i)
	{
		node[i].pre=i;
		node[i].rela=0;
	}
}
int find(int n)
{
	if(node[n].pre==n)
		return n;
	else
	{
		int tpre;
		tpre=node[n].pre;
		node[n].pre=find(node[n].pre);
		node[n].rela=(node[n].rela+node[tpre].rela)%2;
		return node[n].pre;
	}
}
bool joint(int a,int b)
{
	int fa=find(a),fb=find(b);
	if(fa==fb)
		return ((node[b].rela-node[a].rela+2)%2==1);
	else
	{
		node[fb].pre=fa;
		node[fb].rela=(node[a].rela+1-node[b].rela+2)%2;
		return true;
	}
}
int main(void)
{
	int n,m,i,a,b,c;
	while (~scanf("%d%d",&n,&m))
	{
		init();
		bool no_same=true;
		for (i=0; i<m; ++i)
		{
			scanf("%d%d",&a,&b);
			if(!no_same)
				continue;
			no_same=joint(a,b);
		}
		puts(no_same?"No":"Yes");
	}
	return 0;
}



Problem D: Hkhv修路记


Time Limits:  1000 MS   Memory Limits:  65536 KB

64-bit interger IO format:  %lld   Java class name:  Main


Description

Hkhv是一个修路师傅,现在有n个村庄,上头指派任务给他。任务是使任意两个村庄都可以实现公路交通(不一定要村庄之间直连,只要能间接通过公路可达即可)。
现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。问你现在使所有村庄之间畅通的最低成本。

Input

题目有多组测试数据。每个测试用例的第1行给出村庄数目N ( 1< N < 100 )。
随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态。
每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本cost(0 < cost <= 10^7),以及修建状态:1表示已建,0表示未建。

Output


每个测试用例的输出占一行,输出所有村庄畅通需要的最低成本。

Sample Input

3
1 2 1 0
1 3 2 0
2 3 4 0
3
1 2 1 0
1 3 2 0
2 3 4 1
3
1 2 1 0
1 3 2 1
2 3 4 1

Output for Sample Input

3
1
0

福利题之一,最小生成树模版题,但是输入要处理好,一开始我想多了调了好一会儿才过……中间还有个莫名其妙的RE……我也是醉了

代码:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define MM(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=10010;
struct info
{
	int from;
	int to;
	int cost;
	int done;
};
bool cmp(const info &a,const info &b)
{
	return a.cost<b.cost;
}
int pre[N],ran[N];
info pos[N];
void init()
{
	for (int i=0; i<N; ++i)
	{
		pre[i]=i;
		ran[i]=1;
	}
}
int find(int n)
{
	if(pre[n]!=n)
		return pre[n]=find(pre[n]);
	return pre[n];
}
int joint(int a,int b)
{
	int fa=find(a),fb=find(b);
	if(fa!=fb)
	{
		if(ran[fa]>=ran[fb])
		{
			ran[fa]+=ran[fb];
			ran[fb]=0;
			pre[fb]=fa;
		}
		else
		{
			ran[fb]+=ran[fa];
			ran[fa]=0;
			pre[fa]=fb;
		}
		return 1;
	}
	return 0;
}
int main(void)
{
	int n,m,i,j,a,b,c,d;
	while (~scanf("%d",&n))
	{
		init();
		info t;
		int cnt=0;
		int l=(n*(n-1))/2;
		for (i=0; i<l; ++i)
		{
			scanf("%d%d%d%d",&t.from,&t.to,&t.cost,&t.done);
			if(t.done)
			{
				joint(t.from,t.to);
				continue;
			}	
			pos[cnt++]=t;		
		}
		sort(pos,pos+cnt,cmp);
		int r=0;
		for (i=0; i<cnt; ++i)
		{
			if(joint(pos[i].from,pos[i].to))
					r+=pos[i].cost;
		}
		printf("%d\n",r);
	}
	return 0;
}


Problem E: 构造回文串


Time Limits:  1000 MS   Memory Limits:  65536 KB

64-bit interger IO format:  %lld   Java class name:  Main


Description

回文串大家都不陌生了,现在MCA有一个问题关于回文串的问题。给你一个都是小写字母的字符串,字符串内的字母可以任意交换位置,不算作操作次数。
你可以改变其中的字符变成另一个字符,算作一次操作。操作可以无限次。问题来了,在操作次数最小的前提下,使得字符串变成字典序最小的回文串.
 

Input

有多组测试数据(不超过100组),每组数据输入一个都是小写字母的字符串s(1 <= |s| <= 1000).

Output

对于每组数据,输出对应的字典序最小的回文串.

Sample Input

aabc
aabcd

Output for Sample Input

abba
abcba

蛋疼的YY题,首先题目很友好,说是字符串内部位置随意变化,那题目就变成了给你一堆无序的字母然后通过最小的变换叫你凑个回文串,那么到底如何变化最少地来构造一个回文串呢?第一步肯定要给每一个字符出现次数进行计数,然后看每一个字符有奇数个还是偶数个,如果是偶数个最好,直接分两边,奇数个呢,先逆序找到奇数个的字母,再正序找到另一个也是奇数的字母各拿一个进行抵消,剩下的各自都分到两边,那问题就是拿出来的两个到底怎么变?题目说要最小字典序,那肯定要变的跟出现的字母尽量靠近,大的往小的变,当然如果一开始就只有一个奇数个数的字母,当然不需要处理,直接拿一个放中间然后两边排起来输出即可,奇数个数字母大于两种的情况再按照上面的步骤处理;

代码:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define MM(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=4010;
char s[N];
int cnt[30];
int ji[30];
int ou[30];
void init()
{
	MM(s,0);
	MM(cnt,0);
	MM(ji,0);
	MM(ou,0);
}
int main(void)
{
	int i,j,k,len,n,m,l;
	while (~scanf("%s",s))
	{
		l=0;
		len=strlen(s);
		for (i=0; i<len; ++i)
			cnt[s[i]-'a']++;
		for (i=0; i<26; ++i)
		{
			if(cnt[i]&1)
			{
				ji[i]=1;
				l++;
			}	
			else if(cnt[i]&&cnt[i]%2==0)
				ou[i]=1;
		}
		if(l>1)
		{
			for (i=25; i>=0; --i)
			{
				if(cnt[i]&1)
				{
					for (j=0; j<26; ++j)
					{
						if(ji[j])
						{
							ji[j]=ji[i]=0;
							cnt[j]++;
							cnt[i]--;
							l--;
							break;
						}
					}
				}
				if(l<=1)
					break;
			}
		}
		string a="",b="",c="";
		for (i=0; i<26; ++i)
		{
			if(!cnt[i])
				continue;
			if(cnt[i]&1)
			{
				cnt[i]--;
				c+=(char)(i+'a');
			}	
			if(cnt[i]%2==0)
			{
				int w=cnt[i]>>1;
				while (w--)
				{
					a+=(char)(i+'a');
					b+=(char)(i+'a');
				}
			}
		}
		reverse(b.begin(),b.end());
		a=a+c+b;
		cout<<a<<endl;
		init();
	}
	return 0;
}


Problem F: 这真是一道水题


Time Limits:  1000 MS   Memory Limits:  65536 KB

64-bit interger IO format:  %lld   Java class name:  Main


Description

这真的是一道水题。两串字符串比较,简单吧。给你两串字符串,问其中一个字符串交换一次不同位置的两个字符后,能否跟另一个字符串相同。

Input

测试有多组,第一行输入一串字符串,第二行也输入一串字符串,字符串只有小写字母。(1 <= len <= 100000)

Output

如果交换后可以相等则输出"YES",否则输出“NO”

Sample Input

abc
bac
abc
cab

Output for Sample Input

YES
NO


真·福利题,当然这么简单的题目肯定有坑点的啦(还好我没入坑)一开始肯定是想从前往后统计出现不同的字符是否大于2,当然这显然是不够的,如果是一个ab,一个cd,也是两个不同,但是这个就无法通过交换位置得到了,那显然还需要一个条件——两个串里的各个字母个数要相同,那各sort一遍然后strcmp一下就搞定了

代码:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define MM(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=100010;
char a[N],b[N];
int main(void)
{
	while (~scanf("%s%s",a,b))
	{
		int la=strlen(a);
		int lb=strlen(b);
		if(la!=lb)
			puts("NO");
		else
		{
			int flag=0;
			for (int i=0; i<la; ++i)
			{
				if(a[i]!=b[i])
					flag++;
			}
			sort(a,a+la);
			sort(b,b+lb);
			puts((flag<=2&&!strcmp(a,b))?"YES":"NO");
		}
	}
	return 0;
}


Problem G: 又是字符串


Time Limits:  1000 MS   Memory Limits:  65536 KB

64-bit interger IO format:  %lld   Java class name:  Main


Description

又是一道字符串的题目,杨神很想知道一串字符串的哪些前缀是跟后缀是相同的,并且想知道这些前缀最后一个字符的下标。

Input

测试有多组,输入一串只有小写字母组成的字符串,( 1<= len <=400000)

Output

输出前缀最后一个字符的下标,并且从小到大排列,中间以空格隔开,最后一个没有空格

Sample Input

ababcababababcabab
aaaaa

Output for Sample Input

2 4 9 18
1 2 3 4 5

最后15分钟找到的规律,根据next数组性质——next值"前缀"和"后缀"的最长的共有元素的长度,那再根据next表看一下,迭代记录一下即可。此题调试很久,IDE崩溃好几次……F9都快烂了(智商不够的表现)

代码:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define MM(x,y) memset(x,y,sizeof(x))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=400010;
char s[N];
int next1[N];
int ans[N];
void init()
{
	MM(s,0);
	MM(next1,0);
	MM(ans,0);
}
void getnext(char ss[],int next[])
{
	int j=0,k=next[0]=-1;
	int len=strlen(ss);
	while (j<len)
	{
		if(ss[j]==ss[k]||k==-1)
			next[++j]=++k;
		else
			k=next[k];
	}
}
int main(void)
{
	int i,j,k,n;
	while (~scanf("%s",s))
	{
		int cnt=0;
		getnext(s,next1);
		int len=strlen(s);
		int now=len;
		while (now!=0)
		{
			ans[cnt++]=now;
			now=next1[now];
		}
		sort(ans,ans+cnt);
		for (i=0; i<cnt; ++i)
			printf("%d%s",ans[i],i==cnt-1?"\n":" ");
		init();
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值