大一寒假訓練
阿新 • • 發佈:2021-01-31
技術標籤:每日學習筆記
期末考試加軍訓,大概一個月沒碰了
現在寒假開啟了,開始訓練
集訓前有三個星期,就刷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……
慢慢來吧