暑假訓練round 3 題解

今天做題運氣出奇的好,除了幾處小錯誤調試之後忘記改掉了……最後還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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章