2020牛客暑期多校训练营(第三场) 解题报告

L

温暖的签到题。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

string s;

int main(){
	cin>>s;

	for(int i=0;i<s.length();i++){
		if('A'<=s[i]&&s[i]<='Z') s[i]=s[i]-('A'-'a');
	}

	if(s[0]=='l'&&s[1]=='o'&&s[2]=='v'&&s[3]=='e'&&s[4]=='l'&&s[5]=='y') printf("lovely\n");
	else printf("ugly\n");
}

A

温暖的签到题。显然有🐟就肯定🎣。如果没有🐟和鱼饵,那么要么消耗一个鱼饵来增加一条🐟;如果没有🐟但是有鱼饵则做一个鱼饵。最后答案显然是之前钓到的🐟加上鱼饵剩余数量的一半。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=2e6+7;

int x,Ans;
char s[N];

int main(){
	int T=input();

	while(T--){
		int n=input();

		scanf("%s",s+1);
		x=0,Ans=0;

		for(int i=1;i<=n;i++){
			if(s[i]=='0'){
				if(x) Ans++,x--;
			}
			if(s[i]=='1'){
				x++;
			}
			if(s[i]=='2'||s[i]=='3') Ans++;
		}

		printf("%d\n",Ans+x/2);
	}
}

B

温暖的签到题。一开始以为是treap裸题,队友说他会做,就丢给队友了。赛后发现只有把整个字符串想象成一个环,再定义一个起始位置在环上滑动就能维护答案了。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=2e6+7;

char s[N];

int main(){
	scanf("%s",s);

	int pos=0,len=strlen(s);
	int Q=input();
	while(Q--){
		char S[2];

		scanf("%s",S);
		int k=input();

		if(S[0]=='A'){
			printf("%c\n",s[(pos+k-1+len)%len]);
		}else{
			pos=(pos+k+len)%len;
		}
	}
}

C

我们找三条比较有特征的边,大拇指外侧,底侧,小指外侧长度分别为6,9,8。用叉积判断点是顺时针还是逆时针给出的,然后看顺时针/逆时针方向上这三条边顺序,即可判断左右手。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

#define PII pair <double,double>
#define fr first
#define sc second
#define mp make_pair

const int N=27;

PII p[N];

double poly(){
    double area=0;
    for(int i=0,j;i<20;++i){
        j=(i+1)%20;
        area+=p[i].fr*p[j].sc;
        area-=p[i].sc*p[j].fr;
    }
    return area;
}

bool cmp(double a,double b){
    if(fabs(a-b)<1e-4) return 1;
    return 0;
}

double cal(PII a,PII b){
	return (a.fr-b.fr)*(a.fr-b.fr)+(a.sc-b.sc)*(a.sc-b.sc);
}

int get(int x){
	return x%20;
}

void Solve(){
	string Ans1="right",Ans2="left";

	for(int i=0;i<20;i++) scanf("%lf%lf",&p[i].fr,&p[i].sc);

	if(poly()<0) swap(Ans1,Ans2);

	for(int i=0;i<20;i++){
		if(cmp(cal(p[get(i)],p[get(i+1)]),36)&&cmp(cal(p[get(i+2)],p[get(i+3)]),64)){
			cout<<Ans1<<endl;
			return;
		}
		if(cmp(cal(p[get(i)],p[get(i+1)]),64)&&cmp(cal(p[get(i+2)],p[get(i+3)]),36)){
			cout<<Ans2<<endl;
			return;
		}
	}
}

int main(){
	int T=input();
	while(T--){
		Solve();
	}
}

E

动态规划。大概就是发现答案只会有长度为4的结构和长度为6的子结构,然后就可以愉快的进行动态规划了。具体原理我室友的题解吧。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=2e6+7;

ll a[N],dp[N];

int main(){
	int T=input();
	while(T--){
		int n=input();

		for(int i=1;i<=n;i++){
			a[i]=input();
			dp[i]=1e18;
		}
		dp[0]=0;
		
		sort(a+1,a+1+n);

		for(int i=4;i<=n;i++){
			if(i%2==0){
				dp[i]=min(dp[i],dp[i-4]+a[i]-a[i-3]+a[i-1]-a[i-2]);
				dp[i]=min(dp[i],dp[i-4]+a[i]-a[i-2]+a[i-1]-a[i-3]);

				if(i>=6) dp[i]=min(dp[i],dp[i-6]+a[i]-a[i-2]+a[i-1]-a[i-4]+a[i-3]-a[i-5]);
			}
		}

		ll Ans=0;

		for(int i=1;i<=n;i+=2) Ans+=a[i+1]-a[i];

		printf("%lld\n",Ans+dp[n]);
	}
}

G

并查集。看到本题第一想法就是并查集,由于题目中合并两个集合的条件是两集合之间的点有边相连,那么我们考虑怎样的点,对增加集合元素有贡献。

首先已经被扫过的点显然对答案没有贡献,我们发现每次扩展出新的点可能对答案会产生贡献,我们不妨称之为可扩展点或者说是边界点。对于一个集合我们可以开一个队列保存有哪些边界点,每一次向外扩展,现当于把队列内的边界点取出往外扩展一层,并且加入的集合。我们不难发现这个过程有点类似于bfs,其实向外扩展一层现当于向外执行一层bfs,因此我们每次往外扩展时采用这种类bfs的策略来找到需要合并哪些集合。但是这题卡空间,我们无法开\(8*10^5\)\(queue <int>\),那么我们需要手写用链表实习队列。

对于集合合并我们必须保持复杂度,必须同时使用路径压缩和按秩合并。那么我们就面临一个问题:染色。我们知道在按秩合并的过程中我们无法保证某个集合的颜色为\(o_i\)那么我们只需要开个数组或者map维护一下每个集合的颜色就行了(一开始所有集合的颜色都是自己)。

关于此题还有一个细节需要注意,对于一个集合\(S_1\)如果其已经被一个集合\(S\)合并,我们应当认为\(S_1\)是空的,即当\(o_i==S_1\)时该步操作我们无需执行任何操作。具体说明可以见样例解释。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

#define pb push_back

const int N=8e5+1;

struct node{
	int w;
	node *next;
};

struct Qu{
	node *head,*end,*del;

	int front(){
		return head->w;
	}

	void push(int x){
		if(head==NULL&&end==NULL) head=end=new(node);
		else{
			end->next=new(node);
			end=end->next;
		}
		end->w=x;
	}

	void pop(){
		del=head;
		if(head==end) head=end=NULL;
		else head=head->next;
		delete(del);
	}

	bool empty(){
		return head==NULL&&end==NULL;
	}
}q[N];

int fa[N],rk[N];

int find(int x){ return fa[x]==x? x:(fa[x]=find(fa[x]));} 
void merge(int x,int y){
	x=find(x),y=find(y);

	if(x!=y){
		if(rk[x]>=rk[y]){
			while(!q[y].empty()) q[x].push(q[y].front()),q[y].pop();
			fa[y]=x,rk[x]+=rk[y];
		}else{
			while(!q[x].empty()) q[y].push(q[x].front()),q[x].pop();
			fa[x]=y,rk[y]+=rk[x];
		}
	}
}

vector <int> G[N];
Qu tmp;
map <int,int> mp;
int n,m;

void Solve(){
	n=input(),m=input();

	mp.clear();
	for(int i=1;i<=n;i++){
		while(!q[i].empty()) q[i].pop();q[i].push(i);
		rk[i]=1,fa[i]=i;G[i].clear();
		mp[i]=i;
	}
	

	for(int i=1;i<=m;i++){
		int u=input()+1,v=input()+1;
		G[u].pb(v),G[v].pb(u);
	}

	int QQ=input();
	for(int i=1;i<=QQ;i++){
		while(!tmp.empty()) tmp.pop();
		int u=input()+1,tu;
		tu=u;
		if(u!=mp[find(u)]) continue;
		u=find(u);
		while(!q[u].empty()) tmp.push(q[u].front()),q[u].pop();
		// cout<<i<<":"<<tmp.front()<<endl;
		while(!tmp.empty()){
			int t=tmp.front();tmp.pop();
			for(auto v:G[t]){
				merge(v,t);
			}
		}

		mp[find(u)]=tu;
	}

	for(int i=1;i<=n;i++){
		printf("%d%c",mp[find(i)]-1,i==n? '\n':' ');
	}
}

int main(){
	int T=input();
	while(T--)
		Solve();
}
posted @ 2020-07-18 23:57  _aether  阅读(421)  评论(0编辑  收藏  举报