1. 程式人生 > 實用技巧 >2020hdu多校第七場比賽及補題

2020hdu多校第七場比賽及補題

1009Increasing and Decreasing

一個n的全排列,它的最長上升子序列長度為x,它的最長下降子序列長度為y,讓你構造一個這樣的字典序最小的全排列

這簽到題好難,我也只是猜出來的結論,也不怎麼清楚怎麼證明,我的思路就是分為 x 個下降子序列,比如x=3,y=5時,若n = 15我構造出來的是 5 4 3 2 1 10 9 8 7 6 15 14 13 12 11,若n = 14,我構造出來的是 4 3 2 1 10 9 8 7 6 15 14 13 12 11,就是這種構造法

#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1e5+7;
int ans[MAXN];
int lolo[MAXN];
int main()
{
	int T, n, x, y;
	cin >> T;
	while(T--){
		cin >> n >> x >> y;
		if(x+y>n+1){
			cout<<"NO"<<endl;
			continue;
		}
		for (int i = 1;i <= n;i++) ans[i] = 0;
		bool flag = false;
		int cnt = 0;
		int lis = 0;
		int cs = n/y;
		if(y * cs < n) cs++;
		if(x < cs){
			cout<<"NO"<<endl;
			continue;
		} 
		int su = n - x; 
		for(int i = 1;i <= x;i++){
			lolo[i] = 1;
		}
		for(int i = x;i;i--){
			if(su >= y - 1){
				su -= y - 1;
				lolo[i] += y - 1;
			}
			else if(su){
				lolo[i] += su;
				su = 0;
			}
			else break;
		}
		int pos = 0;
		for(int i = 1;i <= x;i++){
			for(int j = 1;j <= lolo[i];j++){
				pos++;
				ans[pos] = cnt + lolo[i] - j + 1;
			}
			cnt += lolo[i];
			//cout<<lolo[i]<<endl;
		}
		cout<<"YES"<<endl;
		for(int i = 1;i < n;i++){
			printf("%d ",ans[i]);
		}
		printf("%d\n",ans[n]);
	}
	return 0;
} 

  

1010Jogging

一個無限大的二維地圖,一個格子座標為(x,y),若x,y互質,那這個格子就是牆,否則這個格子就是一個空地

現給一個初始座標,每次跳遠可以等概率的跳到周圍八格的空地和自身的格子裡(周圍八格有z格空地,那跳到這z格的概率分別為1/(z+1),跳到自己身上的一格的概率也是1/(z+1) )

問跳躍無數次後,回到初始座標的概率

分析:

  • 首先,座標互質的格子為牆,其餘為空地,和一個叫恐怖稽器人的題一樣,有著一些性質:
    • 1、對角線上的格子,除了(1,1),全是空地,所以如果可以跳到對角線,那麼就可以跳到對角線上所有的格子(無限)
    • 2、如果不能跳到對角線,那麼可以跳到的格子是很有限的
  • 上面的兩個性質可以把一部分地圖輸出出來,觀察得到,那麼如果能跳到對角線,回到初始座標的概率就為0,如果周圍八格都是牆,概率為1,else,概率為0到1之間的分數

計算:

概率為(0,1]時,該怎麼計算這個概率,我做的時候感覺自己算不來,因為我概率題沒學過,就先看其他題了,沒多想這個,雖然沒多想,但我還是觀察到一個東西的,在一個樣例中:

1號格子能跳到1號,2號

2號格子能跳到1號,2號,3號

3號格子能跳到2號,3號

初始座標在1號格子

樣例的答案時2 / 7

1號格子能跳到2個格子

2號格子能跳到3個格子

3號格子能跳到2個格子

2 / 2 + 3 + 2恰好等於 2 / 7

當時我感覺應該不是這麼簡單的,後來突然想要判斷這個計算方法是否正確

然後我還真的證出就是這樣算的

#include<iostream>
#include<algorithm>
using namespace std;
bool vis[400][400];
long long res[400][400];
long long opx[8] = {-1,-1,-1,0,0,1,1,1};
long long opy[8] = {-1,0,1,-1,1,-1,0,1};
long long gcd(long long a,long long b){
	if(a<b) swap(a,b);
	while(b){
		a = a % b;
		swap(a,b);
	}
	return a;
}
int fz,fm;
long long sx, sy;
bool flag;
void dfs(long long xx,long long yy){
	vis[200+xx][200+yy] = true;
	res[200+xx][200+yy] = 1;
	for(int i = 0;i < 8;i++ ){
		long long dx = xx + opx[i], dy = yy + opy[i];
		if(gcd(sx + dx,sy + dy) > 1){
			res[200+xx][200+yy]++;
			if(vis[200+dx][200+dy]) continue;
			if(sx + dx == sy + dy){
				flag = false;
				return;
			} 
			dfs(dx,dy);
			if(sx + dx == sy + dy){
				flag = false;
				return;
			} 
		}  
	}
	//cout<<res[200+xx][200+yy]<<endl;
	fm += res[200+xx][200+yy];
}
int main()
{

	int T;
	cin >> T;
	while(T--){
		cin>>sx>>sy;
		fz = fm = 0;
		for(int i = 0;i < 400;i++){
			for(int j = 0;j < 400;j++){
				vis[i][j] = false;
				res[i][j] = 0;
			}
		}
		flag = true;
		dfs(0,0);
		if(!flag) {
			cout<<"0/1"<<endl;
			continue;
		}
		fz = res[200][200];
		long long g = gcd(fz,fm);
		fz /= g;fm /= g;
		cout<<fz<<"/"<<fm<<endl;
	}
	return 0;
}

  

1004Decision

難的一匹OAO,補題補了好久

題意:給定t,a,c,m。2t<=m

u,v是在[0,t]範圍內的隨機的數(取各個數字的概率相同),X[0] = u + v, X[i] = (X[i-1] * a + c) % m (i >= 1),如果X[ | u-v| ] 為偶數則獲勝,問獲勝的概率

看到這個,我是想到會成一個環,然後自己就開始寫了,寫了好長,然後樣例都過不了,才發現這不是一個環

看了題解才知道這是一個環套樹森林,我自己寫的程式碼又長又爛又錯的離譜,看了下std,很短很簡潔orz, 果然acmer與acmer的體質是不能一概而論的

正解是倍增,這種思路我很少用到,寫的時候把人寫傻了

#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1e6+7;
int fa[20][MAXN];
long long dp[20][MAXN];
long long fz,fm;
void cnt(int p,int k){ 
	for(int i = 19;i >= 0;i--){
		if((1<<i)>k) continue;
		fz += dp[i][p] * 2;
		k -= 1<<i;
		p = fa[i+1][p];
	}
}
long long gcd(long long a,long long b){
	if(a<b) swap(a,b);
	while(b){
		a = a % b;
		swap(a,b);
	}
	return a;
}
int main()
{
	int T;
	cin>>T;
	long long t,a,c,m;
	while(T--){
		cin>>t>>a>>c>>m;
		fz = 0;
		for(long long i = 0;i < m; i++) {
			fa[0][i] = (i * a + c) % m;
			dp[0][i] = (fa[0][i] % 2 == 0);
		}
		for(int i = 1;i < 20; i++){
			for(int j = 0;j < m;j++){
				fa[i][j] = fa[i-1][fa[i-1][j]];
				dp[i][j] = dp[i-1][j] + dp[i-1][fa[i][j]];
			}
		}	
		for(int i = 0;i <= t;i++){
			if(i%2) cnt(i,i/2+1);
			else {
				fz++;
				cnt(fa[0][i],i/2);
			}
		}
		for(int i = t + 1;i <= 2 * t;i++){
			if(i%2) cnt(i,(2 * t - i) / 2 + 1);
			else {
				fz++;
				cnt(fa[0][i],(2 * t - i) / 2);
			} 
		}
		fm = (t+1)*(t+1);
		long long gg = gcd(fz,fm);
		fz/=gg;fm/=gg;
		cout<<fz<<"/"<<fm<<endl;
	}
	return 0;
}