文章目錄
1 程序設計基本常識
1.1 關閉cin,cout 同步
使用後不能再使用scanf
|| printf
。
ios::sync_with_stdio(false);
1.2 快速讀入
void read(int &x) {
x = 0;
int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x *= 10;
x += (int) (ch - '0');
ch = getchar();
}
x *= f;
}
1.3 帶模快速冪
typedef long long LL;
LL ksm(LL a, LL p, LL k) {
LL ans = 1;
for (; p; p >>= 1, a = a * a % k)
if (p & 1)
ans = ans * a % k;
return ans;
}
1.4 帶模快速乘
typedef long long LL;
LL ksc(LL a, LL b, LL k) { // a*b%k
LL ans = 0;
while (b) { // 當 b 不爲 0 時繼續循環
if (b & 1) ans = (ans + a) % k;
a = (a + a) % k;
b >>= 1;
}
return ans;
}
2 C++ 與 STL
2.1 輸入輸出
2.1.1 格式串
%1d
只讀1個number char%02d
輸出2位整數不足前導補零%d:%d-%d:%d
格式化讀入%.2f
保留兩位小數%o
八進制%x %#x %#X
十六進制#[^\n]
搭配 getchar() 可讀入一行 (?)
2.1.2 讀入一行 & 字符串相關
printf("%s", stringObject.c_str());
輸出C++ string 轉化成 chargets(aline);
sscanf("123456 ", "%s", str);
sscanf與scanf類似,都是用於輸入的,只是後者以屏幕(stdin)爲輸入源,前者以固定字符串爲輸入源。sprintf(s, "%d", 123);
由於sprintf 跟printf 在用法上幾乎一樣,只是打印的目的地不同而已,前者打印到字符串中,後者則直接在命令行上輸出。這也導致sprintf 比printf 有用得多。
2.2 vector
2.3 stack
2.4 queue & priority_queue
2.5 map
2.6 set
2.7 unordered_map/set
2.8 algorithm sort
3 搜索
3.1 全排列 old
void print() {
for (int i = 1; i <= n; ++i)
printf("%d ", num[i]);
cout << '\n';
}
void dfs(int x) {
if (x > n) {
print();
return;
}
for (int i = 1; i <= n; ++i) {
if (!vis[i]) {
num[x] = i;
vis[i] = 1;
dfs(x + 1);
vis[i] = 0;
}
}
}
3.2 半排列 old
void print() {
for (int i = 1; i <= k; ++i)
printf("%d ", ans[i]);
cout << '\n';
}
void dfs(int x, int last) {
if (x > k) {
print();
return;
}
for (int i = last + 1; i <= n; ++i) {
ans[x] = num[i];
dfs(x + 1, i);
}
}
3.3 dfs子集枚舉 old
void print() {
for (int i = 1; i <= n; ++i)
printf("%d ", vis[i]);
cout << '\n';
}
void dfs(int x) {
if (x > n) {
print();
return;
}
vis[x] = 0;
dfs(x + 1);
vis[x] = 1;
dfs(x + 1);
vis[x] = 0;
}
3.4 二進制子集枚舉
int n; cin >> n;
for (int s = 0; s < (1 << n); ++s) {
for (int j = n - 1; j >= 0; --j)
cout << (bool) (s & (1 << j)) << " ";
cout << '\n';
}
5 二分
5.1 lower_bound & upper_bound
lower_bound
是找到第一個≥x
的位置,upper_bound是找到第一個>x
的位置。
5.2 理解和寫法
二分邊界問題:即l,r
分別用來逼近答案和縮小區間。在這個題中,如果不滿足條件,那麼就l=mid+1
縮小區間,滿足的話r=mid
來逼近。最終符合條件的是mid 或 r
。特別注意的一點:這時計算mid的時候要l + (r-l+1)/2
否則會因爲mid一直==l
,而又check==1
所以死循環。
類似的二分還可以有另外一種寫法:用l
逼近區間,r
來縮小區間,但是這時候注意是左閉右開的,就是那mid正常寫就可了。然後結果l-1的原因:觀察最後小區間的狀態可以得知:當我們檢查到mid
,mid
是中位數時,因爲mid前面的嚴格小於的元素個數是小於mid所在的位置的,所以會執行l = mid+1
,這一句話,所以最終的狀態是 l-1 爲最後我們檢查合法的元素。
5.3 栗子
while (l < r) {
mid = l + (r-l)/2;
// cout << l << ", " << r << "mid = " << mid << endl;
// cout << "now mid = " << mid << endl;
if (check(mid)) { // 如果這個數前面數字達不到中位數個數的情況// 前面如果有小於等於 nums 個數。這個數一定不行
l = mid+1;
} else {// 這個數字可能彳亍
r = mid;
}
}
printf("%d\n",l-1);
while (l < r) {
mid = l + (r-l+1)/2;
// cout << l << ", " << mid << ", " << r << endl;
// cout << "now mid = " << mid << endl;
// 嚴格小於的個數小於真正中位數的位置
if (check(mid)) { // 如果這個數前面數字達不到中位數個數的情況// 前面如果有小於等於 nums 個數。這個數一定不行
l = mid;
} else {// 這個數字可能彳亍
r = mid-1;
}
}
// l-1的原因:觀察最後小區間的狀態可以得知:當我們檢查到mid,mid是中位數時,因爲mid前面的嚴格小於的元素個數是小於mid所在的位置的,
// 所以會執行 l = mid+1,這一句話,所以最終的狀態是 l-1 爲最後我們檢查合法的元素。
printf("%d\n",l);
6 線性數據結構
6.1 單調棧
6.2 單調隊列
7 圖與樹
7.1 存圖鄰接表
memset(first,-1,sizeof(first));
void build(int ff, int tt, int dd) {
es[++tot] = (edge){ff,tt,dd};
nxt[tot] = first[ff];
first[ff] = tot;
}
7.2 樹的直徑
兩遍dfs
7.3 並查集
int find(int x) {
int t, r = x;
while(r != fa[r]) r = fa[r];
while(x != r) {t=fa[x];fa[x]=r;x=t;}
return r;
}
void lianjie(int x, int y) {
int fx = find(x), fy = find(y);
if(fx != fy) fa[fx] = fy;
}
void check(int x, int y) {
int fx = find(x), fy = find(y);
if(fx != fy) puts("N");
else puts("Y");
}
7.4 最小生成樹
int kruskal() {
int ans = 0;
for(int i = 1; i <= V; ++ i) fa[i] = i;
sort(es+1, es+E+1, cmp);
for(int i = 1; i <= E; ++ i) {
int fu = find(es[i].from);
int fv = find(es[i].to);
if(fu != fv) {
fa[fu] = fv;
ans += es[i].cost;
}
}
return ans;
}
7.5 多源最短路 - Floyd
void floyd() {
for(int k = 1; k <= V; ++ k)
for(int i = 1; i <= V; ++ i)
for(int j = 1; j <= V; ++ j)
dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]);
}
7.6 單源最短路 - Dijkstra
struct zt{
int u, d;
};
bool operator < (zt a, zt b) {
return a.d > b.d;
}
priority_queue <zt> q;
#define INF (1e9)
void Dijkstra(int s) {
fill(dis+1,dis+V+1,INF);
dis[s] = 0;
q.push((zt){s,0});
while(q.size()) {
int x = q.top().u;
q.pop();
if(done[x]) continue;
done[x] = 1;
for(int i = first[x]; i != -1; i = nxt[i]) {
int v = es[i].to;
if(dis[v] > dis[x] + es[i].cost) {
dis[v] = dis[x] + es[i].cost;
q.push((zt){v,dis[v]});
}
}
}
}
7.7 差分約束系統
7.8 強聯通分量
8 dp
8.1 LIS & LCS
LIS是最長上升子序列,LCS是最長公共子序列。
求解LIS時,表示在結尾是的最長答案,所以,條件是當的時候更新,保證有序。
求解LCS時,用來表示序列末尾在之前,序列在之前的最優答案,轉移情況分爲兩種,當的時候,,就相當於加上了當前相等的這個元素;當的時候,不能加加一,需要從之前的狀態取最大值,所以。
for (int i = 1; i <= n; ++i) dp1[i] = 1;
int ans = 1;
for (int i = 2; i <= n; ++i) {
for (int j = 1; j < i; ++j) {
if (A[i] > A[j]) {
dp1[i] = max(dp1[i], dp1[j]+1);
}
}
ans = max(ans, dp1[i]);
}
cout << ans << " ";
for (int i = 0; i < n; ++i) dp2[i][0] = 0;
for (int j = 0; j < m; ++j) dp2[0][j] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (A[i] == B[j]) {
dp2[i][j] = dp2[i-1][j-1] + 1;
} else {
dp2[i][j] = max(dp2[i-1][j], dp2[i][j-1]);
}
}
}
cout << dp2[n][m] << endl;
8.2 揹包問題
for(int i = 1; i <= M; i ++)
for(int j = 1; j <= T; j ++)
if(j-c[i] >= 0)
dp[i][j] = max(dp[i-1][j], dp[i-1][j-c[i]]+w[i]);
else dp[i][j] = dp[i-1][j];
cout << dp[M][T] << endl;
8.3 區間型dp
石子歸併:
for(int i = 1; i <= n; i ++)
scanf("%d", &sum[i]);
for(int i = 1; i <= n; i ++)
sum[i] += sum[i-1];
memset(dp,63,sizeof(dp));
for(int i = 1; i <= n; i ++) dp[i][i] = 0;
for(int i = n-1; i >= 1; i --)
for(int j = i+1; j <= n; j ++)
for(int k = i; k <= j; k ++)
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
cout << dp[1][n] << endl;
8.4 狀態壓縮dp
8.4 單調隊列優化
8.5 樹形dp
9 矩陣
9.1 矩陣乘法
9.2 矩陣快速冪
9.3 求解線性遞推
9.4 矩陣快速冪優化dp
10 字符串
10.1 hash
std::hash:
hash<string> my_hash;
unsigned long long iic = (unsigned long long)my_hash("yqynb");
cout << iic << endl;
unsigned long long ull = std::hash<std::string>()( "yqynb" );
cout << ull << endl;
附錄
烏龜棋
for(int i = 0; i < n; ++ i)
scanf("%d", &maps[i]);
for(int i = 1; i <= m; ++ i){int x; scanf("%d", &x); ++ ka[x];}
for(int i = 0; i <= ka[1]; ++ i)
for(int j = 0; j <= ka[2]; ++ j)
for(int k = 0; k <= ka[3]; ++ k)
for(int l = 0; l <= ka[4]; ++ l)
{
int tmp = 0;
if(i) tmp = max(tmp,dp[i - 1][j][k][l]);
if(j) tmp = max(tmp,dp[i][j - 1][k][l]);
if(k) tmp = max(tmp,dp[i][j][k - 1][l]);
if(l) tmp = max(tmp,dp[i][j][k][l - 1]);
dp[i][j][k][l] = tmp + maps[i*1+j*2+k*3+l*4];
}
cout << dp[ka[1]][ka[2]][ka[3]][ka[4]] << endl;
gcd
int gcd(int a, int b) {
if(!b) return a;
return gcd(b,a%b);
}
埃氏篩法
for(int i = 2; i <= n; ++ i)
if(!vis[i])
for(int j = i+i; j <= n; j += i)
vis[j] = 1;
for(int i = 2; i <= n; ++ i)
if(!vis[i])
printf("%d ", i);
printf("\n");
spfa
#define INF (1e9)
queue <int> q;
void spfa(int s) {
fill(dis+1,dis+V+1,INF);
dis[s] = 0;
q.push(s);
used[s] = 1;
while(q.size()) {
int x = q.front();
q.pop();
used[x] = 0;
for(int i = first[x]; i != -1; i = nxt[i]) {
int v = es[i].to;
if(dis[v] > dis[x] + es[i].cost) {
dis[v] = dis[x] + es[i].cost;
if(!used[v]) {
q.push(v);
used[v] = 1;
}
}
}
}
}