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,則答案爲:ans=tipi=ti>pititi>pipi+pi>tipipi>titians=\sum|ti-pi|=\sum_{ti>pi}ti-\sum_{ti>pi}pi+\sum_{pi>ti}pi-\sum_{pi>ti}ti
顯然,題目需要一個O(nlgn)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鴿鴿們再見。

發佈了8 篇原創文章 · 獲贊 8 · 訪問量 1578
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章