1. 程式人生 > 其它 >大一寒假訓練

大一寒假訓練

技術標籤:每日學習筆記

期末考試加軍訓,大概一個月沒碰了

現在寒假開啟了,開始訓練

集訓前有三個星期,就刷cf題吧

目標不高,25道比自己水平高一些的題目

考前兩三天可以練一練模板

搞起。

1.30(複習Tire樹)

做了D. Vasiliy's Multiset

獨立思考了挺久沒做出來

看題解發現要用字典樹

那就複習一波字典樹

就是這樣,通過題目來鞏固相關知識點

Tire樹模板

理解為字元字首樹更好一些

可以快速處理字元的字首問題

注意根節點是0

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 1e3 + 10;
int tire[MAXN][30], n, cnt;
bool End[MAXN];

void add(char *str)
{
	int len = strlen(str), p = 0;
	REP(i, 0, len)
	{
		int id = str[i] - 'a';
		if(!tire[p][id]) tire[p][id] = ++cnt; 
		p = tire[p][id];
	}
	End[p] = true;
}

bool search(char *str)
{
	int len = strlen(str), p = 0;
	REP(i, 0, len)
	{
		int id = str[i] - 'a';
		if(!tire[p][id]) return false;
		p = tire[p][id];
	}
	return End[p];
}

int main()
{
	char str[100];
	scanf("%d", &n);
	_for(i, 1, n)
	{
		scanf("%s", str);
		add(str);	
	} 
	
	while(~scanf("%s", str))
	{
		if(search(str)) puts("Yes");
		else puts("No");
	}
	
	return 0;
}

hdu 1251

這題的輸入方式比較奇葩

gets遇到回車就停,所以要判斷一下空串

用建立tire樹後樹形dp一次就ok了

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 1e6 + 10;
int tire[MAXN][30], End[MAXN], ans[MAXN], cnt;

int dfs(int x)
{
	int res = End[x];
	_for(i, 0, 25)
		if(tire[x][i])
			res += dfs(tire[x][i]);
	return ans[x] = res;
}

void add(char *str)
{
	int len = strlen(str), p = 0;
	REP(i, 0, len)
	{
		int id = str[i] - 'a';
		if(!tire[p][id]) tire[p][id] = ++cnt;
		p = tire[p][id];
	}
	End[p]++;
}

int search(char *str)
{
	int len = strlen(str), p = 0;
	REP(i, 0, len)
	{
		int id = str[i] - 'a';
		if(!tire[p][id]) return 0;
		p = tire[p][id];
	}
	return ans[p];
}

int main()
{
	char str[100];
	while(gets(str) && *str) add(str);

	dfs(0);
	while(~scanf("%s", str))
		printf("%d\n", search(str));

	return 0;
}

poj 3630

比較裸的一道題

用字典樹處理字符集字首問題

注意開陣列的時候是單詞數乘上每個單詞的長度

不要只開單詞數

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 1e5 + 10;
int tire[MAXN][15], n, cnt, ans;
bool End[MAXN];

void add(char *str)
{
	int len = strlen(str), p = 0, flag = 1, flag2 = 1;
	REP(i, 0, len)
	{
		int id = str[i] - '0';
		if(!tire[p][id]) tire[p][id] = ++cnt, flag = 0;
		p = tire[p][id];
		if(End[p]) flag2 = 0;
	}
	if(flag || !flag2) ans = false;
	End[p] = true;
}

int main()
{
	int T; scanf("%d", &T);
	while(T--)
	{
		cnt = 0; ans = true;
		memset(tire, 0, sizeof(tire));
		memset(End, false, sizeof(End));
		
		scanf("%d", &n);
		_for(i, 1, n)
		{
			char str[15];
			scanf("%s", str);
			add(str);
		}
		
		if(ans) puts("YES");
		else puts("NO");
	}

	return 0;
}

「一本通 2.3 例 2」The XOR Largest Pair

字典樹還可以用來求異或最大值

用到了一些位運算

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 1e5 * 33;
int tire[MAXN][2], p, cnt, n;

void add(int x)
{
	int p = 0;
	for(int i = 30; i >= 0; i--)
	{
		int id = (x >> i) & 1;
		if(!tire[p][id]) tire[p][id] = ++cnt;
		p = tire[p][id];
	}
}

int work(int x)
{
	int p = 0, res = 0;
	for(int i = 30; i >= 0; i--)
	{
		int id = (x >> i) & 1;
		if(tire[p][id ^ 1]) 
		{
			res |= (1 << i);
			p = tire[p][id ^ 1];
		}
		else p = tire[p][id];
	}
	return res;
}

int main()
{
	int ans = 0;
	scanf("%d", &n);
	_for(i, 1, n)
	{
		int x; scanf("%d", &x);
		add(x);
		ans = max(ans, work(x));
	}
	printf("%d\n", ans);
	return 0;
}

CF706D Vasiliy's Multiset

現在回到原來的題

其實學完前面的,這題不難的

其實就是上一題多了一個刪除操作而已

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 2e5 * 33;
int tire[MAXN][2], vis[MAXN], cnt, q; 

void add(int x)
{
	int p = 0;
	for(int i = 30; i >= 0; i--)
	{
		int id = (x >> i) & 1;
		if(!tire[p][id]) tire[p][id] = ++cnt;
		p = tire[p][id];
		vis[p]++;
	}
}       

void erase(int x)
{
	int p = 0;
	for(int i = 30; i >= 0; i--)
	{
		int id = (x >> i) & 1;
		p = tire[p][id];
		vis[p]--;
	}
}   

int work(int x)
{
	int p = 0, res = 0;
	for(int i = 30; i >= 0; i--)
	{
		int id = (x >> i) & 1;
		if(tire[p][id ^ 1] && vis[tire[p][id ^ 1]]) 
		{
			res |= (1 << i);
			p = tire[p][id ^ 1];
		}
		else p = tire[p][id];
	}
	return max(x, res);
}        

int main()
{
	scanf("%d", &q);
	while(q--)
	{
		char op[5]; int x;
		scanf("%s%d", op, &x);
		if(op[0] == '+') add(x);
		if(op[0] == '-') erase(x);
		if(op[0] == '?') printf("%d\n", work(x));
	}
	return 0;
}

CF706C Hard problem

中途去做了一道有點水的dp題

我用的字元陣列做的,一些操作挺麻煩的

用C++的string做很多操作會好寫很多

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int MAXN = 1e5 + 10;
char now[MAXN], pre1[MAXN], pre2[MAXN], t1[MAXN], t2[MAXN];
ll dp[MAXN][2];
int c[MAXN], n; 

void copy(char a[], char b[])
{
	memset(a, 0, sizeof(a));
	strcpy(a, b);
}

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &c[i]);
	_for(i, 1, n)
	{
		scanf("%s", now); 
		copy(t1, now);
		reverse(now, now + strlen(now));
		copy(t2, now);
		
		dp[i][0] = dp[i][1] = 1e18;
		if(strcmp(pre1, t1) <= 0) dp[i][0] = min(dp[i][0], dp[i-1][0]);
		if(strcmp(pre2, t1) <= 0) dp[i][0] = min(dp[i][0], dp[i-1][1]);
		if(strcmp(pre1, t2) <= 0) dp[i][1] = min(dp[i][1], dp[i-1][0] + c[i]);
		if(strcmp(pre2, t2) <= 0) dp[i][1] = min(dp[i][1], dp[i-1][1] + c[i]);

		copy(pre1, t1);
		copy(pre2, t2);
	}

	printf("%lld\n", min(dp[n][0], dp[n][1]) == 1e18 ? -1 : min(dp[n][0], dp[n][1]));
	
	return 0;
}

用string寫了一遍

可以直接賦值直接比較,可以用加法串聯,swap等等

但是要用cin,要解綁才不會效率低

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int MAXN = 1e5 + 10;
string s[MAXN][2];
ll dp[MAXN][2];
int c[MAXN], n; 

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	
	cin >> n;
	_for(i, 1, n) cin >> c[i];
	_for(i, 1, n)
	{
		cin >> s[i][0];
		s[i][1] = s[i][0];
		reverse(s[i][1].begin(), s[i][1].end());
		
		dp[i][0] = dp[i][1] = 1e18;
		if(s[i-1][0] <= s[i][0]) dp[i][0] = min(dp[i][0], dp[i-1][0]);
		if(s[i-1][1] <= s[i][0]) dp[i][0] = min(dp[i][0], dp[i-1][1]);
		if(s[i-1][0] <= s[i][1]) dp[i][1] = min(dp[i][1], dp[i-1][0] + c[i]);
		if(s[i-1][1] <= s[i][1]) dp[i][1] = min(dp[i][1], dp[i-1][1] + c[i]);
	}

	if(min(dp[n][0], dp[n][1]) == 1e18) cout << "-1" << endl;
	else cout << min(dp[n][0], dp[n][1]) << endl;
	
	return 0;
}

poj 3764

想半天不知道圖上路徑怎麼處理

看題解發現要用lca的思想

把路徑轉化為兩個點到根節點,這樣就變成上一道題了

這個真忘記了,沒辦法,有時候一直卡住沒有思路是因為需要用到的演算法是知識盲區

lca我又已經忘記了

那就明天開始複習lca吧

反正現在就做題,哪裡不會補哪裡

通過題目來鞏固知識點

現在Tire樹這個知識點我已經掌握了

感覺還有好多演算法我要補

二分圖,狀壓dp,lca,kmp……

慢慢來吧