2020HHUACM寒假训练第一周题目总结

2020HHUACM寒假训练第一周题目总结

小小总结一下第一周寒假训练的题目。
我作为出题人,拉了七道题,但是目前自己没有补完,属实汗颜,写这篇博客也是督促我早日补完剩下的三道题。

C - Domino for Young

题目情况:某次div2的D题,一开始以为是个很有难度的玄学dp,后来看了qsc的题解视频发现是道很牛的思维题,作为本次三道签到题之一,过题数量全场最多,有13人通过了,感觉很成功。并且,有牛逼学弟用了牛逼贪心过了,属实令我没想到。
题目大意:给出一个n列不规则的棋盘,保证每一列的高度不高于前面一列,问最多能塞下几个1*2的多米诺骨牌。
题目解析:该题就是考了一个结论,对整个棋盘进行黑白染色,显然,答案就是黑白格子中较小的一个。证明过程用了二分图,出题人有很详细的证明过程,大家可以去看看。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int n;
int a[maxn];
ll b[2];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i%2]+=a[i]/2+a[i]%2;//黑白格子划分 
		b[(i+1)%2]+=a[i]/2;
	}
	printf("%lld\n",min(b[0],b[1]));
	return 0;
}

E - Machine Schedule

题目情况:这题我是最后出的,发现出完前六题之后差一道图论题,本着不为难大家的心理,就出了一道二分图结论的模板题,想着大家可能不知道结论。结果成功被人签到,全场第一个出的题。
题目大意:有两台机器A和B,每台机器都有m种不同的工作模式。有n个任务,每个任务有两个属性xi和yi,表示如果要完成这个任务A机器要是模式xi或者B机器是模式yi。两台机器每切换一次模式需要重启一次,问最少的重启次数。
题目解析:建图,以边为任务,点为工作模式,简单列一下A和B。可以看出题意就是用最少的覆盖这个二分图的每一条边。引入一个结论:二分图上的最小点覆盖等于二分图的最大匹配数。此题就是二分图的模板题了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int n,m,k;
int link[220],vis[220],f[220][220];
int dfs(int x){
	for(int i=1;i<=n;i++){
		if(f[x][i] && !vis[i]){
			vis[i]=1;
			if(!link[i] || dfs(link[i])){
				link[i]=x;
				return 1;
			}
		}
	}
	return 0;
}
int find(){
	memset(link,0,sizeof(link));
	int cnt=0;
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		if(dfs(i))cnt++;
	}
	return cnt;
}
int main(){
	while(~scanf("%d",&n)){
		if(!n)break;
		scanf("%d%d",&m,&k);
		memset(f,0,sizeof(f));
		for(int i=1;i<=k;i++){
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			if(y && z){
				f[y][z]=1;
			}
		}
		printf("%d\n",find());
	} 
	return 0;
}

D-Stone Game, Why are you always there?

题目情况:前不久在牛客上打了某个学校的新生赛,发现SG函数在博弈题里属实有一套,为了在深入理解SG函数的同时促进对博弈题的理解,出了一个不那么裸的SG函数模板题,结果还是被大家秒了,又令我没有想到。
题目大意:给出一个含n个正整数的集合,每次取数只能取这个集合里面的数字。有m轮询问,每次有一排标了号的石子1~ki,只能取走连续的石子,且若取走2号石子,1号石子和3号石子仍旧是不连续的。
题目解析:显然,可以发现是一道关于sg函数的题目,问题就是如何处理两部分分下来的石子。如果取走的不是中间的x个石子,就是把原来的sg[i]转变为sg[i-x],如果取走的是中间的石子,那么就将一个游戏转变为了两个分下来的游戏。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int sg[1010],n,a[1010];
int getsg(int x){
	if(sg[x]!=-1)return sg[x];
	bool vis[1010];
	memset(vis,0,sizeof(vis));
	for(int i=0;i<n;i++){
		if(x>=a[i]){
			for(int j=0;;j++){
				if(j+a[i]<=x && j<x/2+1)
					vis[getsg(j)^getsg(x-a[i]-j)]=1;
				else 
					break;
			}
		}
	}
	for(int i=0;;i++)
		if(!vis[i])return sg[x]=i;
}
int main(){
	while(~scanf("%d",&n)){
		for(int i=0;i<n;i++)scanf("%d",&a[i]);
		int T;
		sort(a,a+n);
		memset(sg,-1,sizeof(sg));
		scanf("%d",&T);
		sg[0]=0;
		while(T--){
			int x;
			scanf("%d",&x);
			if(getsg(x))puts("1");
			else puts("2");
		} 
	}
	return 0;
}

B - K Integers

题目详情:某场div2的E题,本着补题的心态拉了上来。果然没有人做这道题。除了我和负责写题解的学长。
题目大意:给出一个排列a,每次可以交换相邻的元素,问最少多少次交换可以形成一个1~k的子串。
题目解析:将问题分为两个部分。第一部分,考虑一个杂乱的排列51342,需要几步能变成12345。很经典的问题,显然是求出里面的逆序对数量。第二部分,考虑一个数组5991939429,如何才能将51342交换到一起。这个也是个经典问题。感觉比第一部分难一点。我们需要选取一个基准点作为不动的一个点,其他点肯定是往这个点靠。这个点可以用二分来取得。设所有元素初始位置为ti,最终位置为pi,则答案为: a n s = t i p i = t i > p i t i t i > p i p i + p i > t i p i p i > t i t i ans=\sum|ti-pi|=\sum_{ti>pi}ti-\sum_{ti>pi}pi+\sum_{pi>ti}pi-\sum_{pi>ti}ti
显然,题目需要一个 O ( n l g n ) O(nlgn) 的复杂度数据结构。显然线段树/树状数组可以满足我们的全部要求。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int n;
ll a[maxn],f[maxn],b[maxn];
int lowbit(int x){
	return x&(-x);
}
void add(ll *c,int x,int k){
	for(int i=x;i<=n;i+=lowbit(i)){
		c[i]+=1ll*k;
	}
}
ll find(ll *c,int x){
	ll ans=0;
	for(int i=x;i>0;i-=lowbit(i)){
		ans+=c[i];
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x; 
		scanf("%d",&x),f[x]=i;
	} 
	ll sum=0;
	for(int i=1;i<=n;i++){
		ll ans=0;
		sum+=(1ll*i-1-find(a,f[i]));//计算逆序对
		add(a,f[i],1);
		add(b,f[i],f[i]);
		if(i==1)printf("0 ");
		else {
			int l=1,r=n,mid,t;
			while(l<=r){
				mid=(l+r)>>1;
				if(find(a,mid)*2<=i)l=mid+1,t=mid;
				else r=mid-1;
			}
			ll L=find(a,mid),R=i-L;
			ll cnt1=find(b,mid),cnt2=find(b,n)-cnt1;
			ans+=(mid+(mid-L+1))*L/2-cnt1;
			ans+=cnt2-(mid+1+(mid+R))*R/2;//计算聚合数字 
			printf("%lld ",ans+sum);
		}
	}
	return 0;
}

小总结

最后还剩下三道题,争取有时间补了QAQ鸽鸽们再见。

发布了1 篇原创文章 · 获赞 0 · 访问量 125

猜你喜欢

转载自blog.csdn.net/weixin_43014282/article/details/104115685