就这样,我在繁忙的语言考试和期中考试还有科研中抽出时间来写完了这一章,感觉还有好多,但似乎看得到希望。
HDOJ 4699 (对顶栈)
题意:
一个整数序列的编辑器有以下五种操作:
1. I x,在当前光标位置之后插入一个整数x,插入之后光标移动到x之后;
2. D,删除光标之前的一个整数
3. L,光标向左移动一个位置
4. R,光标向有移动一个位置
5. Q k,询问在位置k之前的最大前缀和,其中k不超过当前光标的位置
解法:
堆顶栈,用两个栈来模拟光标的变化,同时用一个数组线性维护最大值,具体的:
对于I x 操作:
1.把x插入栈A;
2.更新sum[pa]=sum[pa-1]+a[pa];
3.更新f[pa]=max(f[pa-1],sum[pa])。
对于D操作,把A的栈顶出栈。
对于L操作,弹出A的栈顶,插入到B中。
对于R操作:
1.弹出B的栈顶,插入到A中。
2.更新sum[pa]=sum[pa-1]+a[pa];
3.更新f[pa]=max(f[pa-1],sum[pa])。
对于Q k询问,直接返回f[k]。
1 #pragma GCC optimize(3) 2 #include3 #include 4 #include 5 #include 6 #include 7 #include 8 #include <string> 9 #include 10 #include 11 #include 12 #include <set> 13 #include
POJ 2559 (单调栈)
题意:
给出一些连续的高度,求最大可以形成的长方形
解法:
单调栈例题;
当遇到一个矩形的高度小于等于当前栈顶元素的高度时,便一直弹出,直到小于位置;
期间用一个变量维护最大值即可;
1 #include2 #include 3 #include 4 #include 5 using namespace std; 6 typedef long long LL; 7 8 struct node { 9 LL val; 10 LL len; 11 }; 12 stack s; 13 14 int main() { 15 int n; 16 while (scanf("%d", &n) && n) { 17 LL ans = 0; 18 node ths; 19 while (!s.empty())s.pop(); 20 for (int i = 0; i < n; i++) { 21 scanf("%lld", &ths.val); 22 ths.len = 1; 23 LL tmp = 0; 24 if (s.empty()) { 25 s.push(ths); 26 continue; 27 } 28 if (ths.val <= (s.top()).val) { 29 while (!s.empty() && ths.val <= (s.top()).val) { 30 (s.top()).len += tmp; 31 LL m = (s.top()).val*(s.top()).len; 32 ans = max(ans, m); 33 tmp = (s.top()).len; 34 s.pop(); 35 } 36 ths.len += tmp; 37 s.push(ths); 38 } 39 else { 40 s.push(ths); 41 } 42 } 43 LL tmp = 0; 44 while (!s.empty()) { 45 (s.top()).len += tmp; 46 LL m = (s.top()).val*(s.top()).len; 47 ans = max(ans, m); 48 tmp = (s.top()).len; 49 s.pop(); 50 } 51 printf("%lld\n", ans); 52 } 53 return 0; 54 }
BZOJ 2457 (队列,思维)
题意:
需要对2*1e5个整数排序,但是只能若干个双端队列来实现,对于每个数A[i],你只能做两件事:
1. 新建一个双端队列,并将A[i]作为这个队列中的唯一的数
2. 把A[i]从已有队列的队头或对尾入队
对所有数完成之后,要求可以将这些队列按照非降的顺序链接起来。求最少需要多少个双端队列?
解法:
这个题首先你得能看出来将所有的数字排序后他们的下标满足单峰函数的性质即可;
怎样验证有多少个单峰函数?这道题的写法非常有代表性(根据性质沿着单峰函数一直走即可)
1 #include2 #include 3 using namespace std; 4 typedef pair<int, int> pii; 5 const int MAXN = 2e5 + 5; 6 const int INF = 0x3f3f3f3f; 7 struct node { int v, id; }a[MAXN]; 8 int mx[MAXN], mn[MAXN]; 9 bool cmp(node a, node b) { return a.v == b.v ? a.id < b.id : a.v < b.v; } 10 11 int main() { 12 int n; scanf("%d", &n); 13 for (int i = 1; i <= n; i++) { 14 scanf("%d", &a[i].v); 15 a[i].id = i; 16 } 17 18 sort(a + 1, a + n + 1, cmp); 19 int cnt = 1, k = 1; 20 mn[k] = a[1].id; 21 for (int i = 2; i <= n; i++) { 22 if (a[i - 1].v != a[i].v) { 23 mx[k++] = a[i-1].id; 24 mn[k] = a[i].id; 25 } 26 } 27 28 bool flag = 0;//down 29 int ths = INF; 30 for (int i = 1; i <= k; i++) { 31 if (flag == 0) { 32 if (ths > mx[i])ths = mn[i]; 33 else { 34 ths = mx[i]; 35 flag = !flag; 36 } 37 } 38 else { 39 if (ths < mn[i])ths = mx[i]; 40 else{ 41 ths = mn[i]; 42 flag = !flag; 43 cnt++; 44 } 45 } 46 } 47 printf("%d\n", cnt); 48 return 0; 49 }
POJ 3349 (手写Hash表)
题意:
给出n片雪花,每片雪花有六个角,每个角由一个数字表示,如雪花A为(1,2,3,4,5,6),问是否有雪花是相同,相同的意思是比如还有雪花B(2,3,4,5,1,6),那么雪花A和B就是一样的
解法:
手写hash表的板子题;
多留心一下怎样检查这几片叶子是否一样;
1 #include2 #include 3 #include 4 #define REP(i,a,b) for(int i=(a);i<(b);i++) 5 #define MAXN (100000+10) 6 #define MOD (99991) 7 using namespace std; 8 9 int head[MAXN], nex[MAXN]; 10 int snow[MAXN][10]; 11 int tot = 0; 12 13 int H(int *a) { 14 int sum = 0, mul = 0; 15 REP(i, 0, 6) { 16 sum = (sum + a[i]) % MOD; 17 mul = (1ll * mul*a[i]) % MOD; 18 } 19 return (sum + mul) % MOD; 20 } 21 22 bool equal(int *a,int *b) { 23 REP(i, 0, 6)REP(j, 0, 6) { 24 bool eq = 1; 25 REP(k, 0, 6) if (a[(i + k) % 6] != b[(j + k) % 6]) 26 eq = 0; 27 if (eq) return eq; 28 eq = 1; 29 REP(k, 0, 6)if (a[(i + k) % 6] != b[(j - k + 6) % 6]) 30 eq = 0; 31 if (eq) return eq; 32 } 33 return 0; 34 } 35 36 int insert(int *a) { 37 int val = H(a); 38 for (int i = head[val]; i; i = nex[i]) { 39 if (equal(snow[i], a))return 1; 40 } 41 42 tot++; 43 memcpy(snow[tot], a, 6 * sizeof(int)); 44 nex[tot] = head[val]; 45 head[val] = tot; 46 return 0; 47 } 48 49 int main() { 50 int n; scanf("%d", &n); 51 while (n--) { 52 int a[10]; 53 REP(i, 0, 6)scanf("%d", &a[i]); 54 if (insert(a)) { 55 printf("Twin snowflakes found.\n"); 56 return 0; 57 } 58 } 59 printf("No two snowflakes are alike.\n"); 60 return 0; 61 }
CH1401 (字符串Hash)
题意:
给定两个字符串,然后每次给出两个区间,看这两个串这两个区间内的序列一样不一样
解法:
字符串hash的板子题; 注意不能通过给两个字符串分别hash,然后直接看他们的不同区间的hash值是否一样;
1 #include2 #include 3 #define MAXN (1000000+5) 4 #define LL long long 5 using namespace std; 6 7 const int TOP = 1e6, N = TOP + 10; 8 const int Z = 1e9 + 7;//取模数 9 const int V = 26;//字符集哈希数 10 char ss[MAXN]; 11 int q; 12 LL v[MAXN]; // [x] = V^x%Z 13 LL u[MAXN];//u[x]=hashvalue(1,x); 14 LL hashvalue(int l, int r){ 15 return (u[r] - u[l - 1] * v[r - (l - 1)] % Z + Z) % Z; 16 } 17 18 int main() { 19 scanf("%s", ss + 1); 20 v[0] = 1; for (int i = 1; i <= TOP; i++)v[i] = v[i - 1] * V%Z; 21 u[0] = 0; for (int i = 1; ss[i]; i++)u[i] = (u[i - 1] * V + ss[i] - 'a') % Z; 22 scanf("%d", &q); 23 while (q--) { 24 int l, r, l1, r1; 25 scanf("%d%d%d%d", &l, &r, &l1, &r1); 26 if (hashvalue(l, r) == hashvalue(l1, r1)) 27 puts("Yes"); 28 else 29 puts("No"); 30 } 31 return 0; 32 }
POJ 1961 (KMP求循环次数)
题意:
给定一个长度为N的字符串S,对S的每一个前缀,若其最大循环次数大于1,则输出该前缀的最小循环元长度和最大循环次数
解法:
首先这道题为根据失配数组累计寻找最长循环节的位置;
其次,那个循环节的判断直接判读是否为循环节而且循环次数大于两次即可;
没有之前想的那么邪乎==
1 #include2 #include<string.h> 3 #include 4 #include 5 using namespace std; 6 int T, len; 7 char a[1000000 + 5]; 8 int nex[1000000 + 5]; 9 int f[1000000 + 5]; 10 11 void get_next(char *a, int *nex) { 12 nex[1] = 0; 13 for (int i = 2, j = 0; i <= len; i++) { 14 while (j > 0 && a[i] != a[j + 1])j = nex[j]; 15 if (a[i] == a[j + 1])j++; 16 nex[i] = j; 17 } 18 return; 19 } 20 21 int KMP(char *a, char *b) { //b为母串 22 for (int i = 1, j = 0; i <= strlen(b + 1); i++) { 23 while (j > 0 && (j == strlen(a + 1) || b[i] != a[j + 1]))j = nex[j]; 24 if (b[i] == a[j + 1])j++; 25 f[i] = j; 26 //若a在b中出现,,返回出现的位置 27 //if(f[i]==strlen(a+1)){return i-strlen(a);} 28 } 29 return 1; 30 } 31 32 int main() { 33 int kase = 0; 34 while (scanf("%d", &len) && len) { 35 memset(nex, 0, sizeof(nex)); 36 scanf("%s", a + 1); 37 get_next(a,nex); 38 printf("Test case #%d\n", ++kase); 39 for (int i = 2; i <= len; i++) { 40 int l = i - nex[i]; 41 if ((i%l) == 0 && i / l > 1) { 42 printf("%d %d\n", i, i / l); 43 } 44 } 45 printf("\n"); 46 } 47 return 0; 48 }
CH1601 (前缀树)
题意:
给定N个字符串Sn,接下来M次询问,每次询问给定一个字符串T,求S1~Sn中有多少个字符串是T的前缀
解法:
字典树查前缀的板子题 由于不同的单词可能到同样的节点结尾,所以需要用一个cnt[]来记录到结点p的单词数
1 #include2 #include 3 #include 4 #define REP(i,a,b) for(int i=(a);i<=(b);i++) 5 #define MAXN 1000000+10 6 using namespace std; 7 8 int trie[MAXN][26], tot = 1; 9 char s[MAXN]; 10 bool ed[MAXN]; 11 int cnt[MAXN]; 12 13 void insert(char *str) { 14 int len = strlen(str), p = 1; 15 for (int k = 0; k < len; k++) { 16 int ch = str[k] - 'a'; 17 if (trie[p][ch] == 0)trie[p][ch] = ++tot; 18 p = trie[p][ch]; 19 } 20 //仅插入不统计数量 21 //ed[p] = true; 22 cnt[p]++; 23 } 24 25 bool search_exist(char *str) { 26 int len = strlen(str), p = 1; 27 for (int k = 0; k < len; k++) { 28 p = trie[p][str[k] - 'a']; 29 if (p == 0)return false; 30 } 31 return ed[p]; 32 } 33 34 int search_pre_num(char* str) { 35 int len = strlen(str), p = 1; 36 int ans = 0; 37 for (int k = 0; k < len; k++) { 38 p = trie[p][str[k] - 'a']; 39 if (p == 0)return ans; 40 ans += cnt[p]; 41 } 42 return ans; 43 } 44 45 int main() { 46 int n,m; scanf("%d%d", &n,&m); 47 REP(i, 1, n) { 48 scanf("%s", s); 49 insert(s); 50 } 51 REP(i, 1, m) { 52 scanf("%s", s); 53 printf("%d\n", search_pre_num(s)); 54 } 55 return 0; 56 }
CH 1602 The XOR Longest Pair(前缀树)
题意:
给定N个整数An,,从中选出两个进行xor运算,得到的结果最大是多少?
解法:
用字典树表示异或和,每次走到一个结点,我们就往相反的节点走(要是有的话); 要是没有的话,就沿着继续走; 枚举每一个数,选最大的那个
1 #include2 #include 3 #include 4 #include 5 #define REP(i,a,b) for(int i=(a);i<(b);i++) 6 using namespace std; 7 const int MAXN = 1e6 + 5; 8 int trie[MAXN << 5][2], tot = 1; 9 bool ed[MAXN << 5]; 10 int a[MAXN]; 11 12 void insert(int n) { 13 int p = 1; 14 for (int k = 31; k >= 0; k--) { 15 int ch = (n >> k) & 1; 16 if (trie[p][ch] == 0)trie[p][ch] = ++tot; 17 p = trie[p][ch]; 18 } 19 ed[p] = true; 20 } 21 22 int search(int n) { 23 int p = 1; 24 int ans = 0; 25 for (int k = 31; k >= 0; k--) { 26 int ch = (n >> k) & 1; 27 if (trie[p][ch ^ 1])ans |= (1 << k), p = trie[p][ch ^ 1]; 28 else p = trie[p][ch]; 29 } 30 return ans; 31 } 32 33 int main() { 34 int n; scanf("%d", &n); 35 REP(i, 0, n) { 36 scanf("%d", &a[i]); 37 insert(a[i]); 38 } 39 int ans = 0; 40 REP(i, 0, n) { 41 ans = max(ans, search(a[i])); 42 } 43 printf("%d\n", ans); 44 return 0; 45 }
POJ 3764 (前缀树)
题意:
一颗树,每个边有个值,在树上找一条简单路径,使得这条路径上的边权异或值最大
解法:
设d[x]=d[father(x)] xor weight(x,father(x)),
先dfs一遍得到所有的d[],然后套之前的板子
问题转换为从d[1]~d[n]中选两个,使得xor结果最大
1 #include2 #include 3 #include 4 #include 5 #include 6 #define REP(i,a,b) for(int i=(a);i<=(b);i++) 7 #define MAXN (200000+5) 8 using namespace std; 9 int vis[MAXN], val[MAXN]; 10 int trie[6122222][2], tot = 1; 11 int e; 12 13 struct node { 14 int v, w, next; 15 }edge[MAXN * 2]; 16 int head[MAXN]; 17 18 void addEdge(int u, int v, int w){ 19 edge[e].v = v; edge[e].next = head[u]; edge[e].w = w; head[u] = e++; 20 edge[e].v = u; edge[e].next = head[v]; edge[e].w = w; head[v] = e++; 21 } 22 23 void dfs(int u, int x) { 24 val[u] = x; 25 vis[u] = 1; 26 for (int i = head[u]; i != -1; i = edge[i].next) { 27 int v = edge[i].v; 28 int w = edge[i].w; 29 if (!vis[v]) { 30 dfs(v, x ^ w); 31 } 32 } 33 } 34 35 void insert(int n) { 36 int p = 1; 37 for (int k = 31; k >= 0; k--) { 38 int ch = (n >> k) & 1; 39 if (trie[p][ch] == 0)trie[p][ch] = ++tot; 40 p = trie[p][ch]; 41 } 42 //ed[p] = true; 43 } 44 45 int search(int n) { 46 int p = 1; 47 int ans = 0; 48 for (int k = 31; k >= 0; k--) { 49 int ch = (n >> k) & 1; 50 if (trie[p][ch ^ 1])ans |= (1 << k), p = trie[p][ch ^ 1]; 51 else p = trie[p][ch]; 52 } 53 return ans; 54 } 55 56 int main() { 57 int n; 58 while (scanf("%d", &n) != EOF) { 59 memset(trie, 0, sizeof(trie)); 60 //memset(val, 0, sizeof(val)); 61 for (int i = 0; i <= n; i++) 62 vis[i] = 0, val[i] = 0; 63 e = 0; 64 memset(head, -1, sizeof(head)); 65 for (int i = 1; i < n; i++) { 66 int u, v, w; 67 scanf("%d%d%d", &u, &v, &w); 68 u++; v++; 69 addEdge(u, v, w); 70 } 71 dfs(1, 0); 72 REP(i, 1, n) insert(val[i]); 73 int ans = 0; 74 REP(i, 1, n) { 75 ans = max(ans, search(val[i])); 76 } 77 printf("%d\n", ans); 78 } 79 return 0; 80 }
POJ 1456 (贪心,优先队列)
题解:
1 #include2 #include 3 #include 4 #define REP(i,a,b) for(int i=(a);i<(b);i++) 5 #define MAXN 10005 6 #define pii pair 7 #define LL long long 8 using namespace std; 9 struct node { 10 int t, v; 11 bool operator < (const node & rhs)const { 12 return v > rhs.v; 13 } 14 }; 15 priority_queue q; 16 pii a[MAXN]; 17 bool cmp(pii a, pii b) { return a.first < b.first; } 18 19 int main() { 20 int n; 21 while (scanf("%d", &n) != EOF) { 22 REP(i, 0, n) scanf("%d%d", &a[i].second, &a[i].first); 23 sort(a, a + n, cmp); 24 25 REP(i, 0, n) { 26 int t = a[i].first; 27 int v = a[i].second; 28 if (q.size() == 0) { 29 q.push(node{ t,v }); 30 continue; 31 } 32 if (t == q.size()) { 33 if (v > q.top().v) { 34 q.pop(); 35 q.push(node{ t,v }); 36 } 37 } 38 else { 39 q.push(node{ t,v }); 40 } 41 } 42 43 LL ans = 0; 44 while (!q.empty()) { 45 ans += 1ll * q.top().v; 46 q.pop(); 47 } 48 printf("%lld\n", ans); 49 } 50 return 0; 51 }
POJ 2442 (优先队列)
题意:
给定M个长度为N的序列,从每个序列中任取一个数求和,可以构成N^M个和,求其中最小的N个和
解法:
首先我们给每个组中的元素都排序,之后我们用一个四元组来维护优先队列,
优先队列中为之前n行中的和最小的n个数;
每次从两个数组中比较,两个数组和最小的n个数,填入新的数组,以此类推;
1 #include2 #include 3 #include 4 #include 5 #define REP(i,a,b) for(int i=(a);i<=(b);i++) 6 #define MAXN 2005 7 #define MAXM 105 8 using namespace std; 9 10 int a[MAXM][MAXN]; 11 int b[MAXN], nb[MAXN]; 12 int n, m; 13 struct node { 14 int i, j, last, val; 15 bool operator < (const node &rhs) const { 16 return val > rhs.val; 17 } 18 }; 19 20 void compose(int *a,int *b) { 21 priority_queue q; 22 q.push(node{ 1,1,false,b[1] + a[1] }); 23 REP(k, 1, n) { 24 int i = q.top().i; 25 int j = q.top().j; 26 int last = q.top().last; 27 int val = q.top().val; 28 nb[k] = val; 29 q.pop(); 30 q.push(node{i, j + 1, true, a[i] + b[j+1]}); 31 if (!last)q.push(node{ i + 1,j,false,a[i + 1] + b[j] }); 32 } 33 return; 34 } 35 36 int main() { 37 int T; scanf("%d", &T); 38 while (T--) { 39 scanf("%d%d", &m, &n); 40 REP(i, 1, m){ 41 REP(j, 1, n)scanf("%d", &a[i][j]); 42 sort(a[i] + 1, a[i] + n + 1); 43 } 44 REP(i, 1, n)b[i] = a[1][i]; 45 46 REP(i, 2, m) { 47 compose(b, a[i]); 48 REP(i, 1, n)b[i] = nb[i]; 49 } 50 sort(b + 1, b + 1 + n); 51 printf("%d", b[1]); 52 REP(i, 2, n)printf(" %d", b[i]); 53 printf("\n"); 54 } 55 return 0; 56 }
CH1701 (二叉哈夫曼树)
题意:
有n堆果子,每次可以合并相邻的两堆,合并的代价是两堆果子的重量,求最少的代价
解法:
基础的哈夫曼树
1 #include2 #include 3 #include 4 #include 5 #define REP(i,a,b) for(int i=(a);i<(b);i++) 6 #define LL long long 7 using namespace std; 8 priority_queue<int, vector<int>, greater<int> > q; 9 10 int main() { 11 int n; scanf("%d", &n); 12 int o; 13 long long ans = 0; 14 REP(i, 0, n) { 15 scanf("%d", &o); 16 q.push(o); 17 } 18 while (q.size() != 1) { 19 LL a = q. top(); q.pop(); 20 LL b = q.top(); q.pop(); 21 ans += (a + b); 22 q.push(a+b); 23 } 24 printf("%lld\n", ans); 25 return 0; 26 }
BZOJ 4198 (K叉哈夫曼树)
题意:
一本书中有n种不同的单词,从1到n进行编号,其中第i种单词出现的总次数为Wi。你想要用k进制串Si来替换第i种单词,使得其满足对于任意的1<=i,j<=n,i != j ,都有Si不是Sj的前缀
解法:
k叉哈夫曼树的解法,先和并的结点深度最大,其次还要学习怎样补充够节点数;
ansv为新的最小总长度,ansd为最短的最大长度;
重载运算符的时候要注意比较的顺序,反一下就相当于将重载符去反。
1 #include2 #include 3 #include 4 #include 5 #define REP(i, a, b) for(int i = (a); i < (b); i++) 6 #define MEM(a,x) memset(a,x,sizeof(a)) 7 #define LL long long 8 #define MOD ((int)1000000007) 9 #define MAXN 200000+5 10 using namespace std; 11 struct node { 12 LL val; 13 int deep; 14 bool operator < (const node & rhs)const { 15 return val == rhs.val ? rhs.deep < deep : rhs.val < val; 16 } 17 }; 18 priority_queue q; 19 20 int main() { 21 int n, k; scanf("%d%d", &n, &k); 22 REP(i, 0, n){ 23 LL o; scanf("%lld", &o); 24 q.push(node{ o,1 }); 25 } 26 int remain = (n - 1) % (k - 1); 27 if (remain != 0) { remain = k - 1 - remain; n += remain; } 28 REP(i, 0, remain)q.push(node{ 0,1 }); 29 30 LL ansv = 0; 31 int ansd = 0; 32 while (n > 1) { 33 int maxd = 0; LL tot = 0; 34 REP(i, 0, k) { 35 tot += q.top().val; 36 maxd = max(maxd, q.top().deep); 37 q.pop(); 38 } 39 q.push(node{ tot,maxd + 1 }); 40 ansv += tot; 41 ansd = max(ansd, maxd); 42 n -= k - 1; 43 } 44 printf("%lld\n%d", ansv, ansd); 45 return 0; 46 }
CH1801 (栈找最长合法括号序列)
题意:
给出一个括号序列,找出最长的一段括号序列,使得其合法
解法:
栈模拟就行了
1 #include2 #include 3 #include 4 #include 5 #define REP(i, a, b) for(int i = (a); i < (b); i++) 6 #define MEM(a,x) memset(a,x,sizeof(a)) 7 #define LL long long 8 #define MOD ((int)1000000007) 9 #define MAXN 200000+5 10 const int INF = 2147483647; 11 const int LLINF = 9223372036854775807; 12 using namespace std; 13 stack<int>st; 14 char c[MAXN]; 15 int main() { 16 scanf("%s", c); 17 int ans = -INF; 18 int tmp = 0; 19 REP(i, 0, strlen(c)) { 20 char o=c[i]; 21 if (st.empty()) { 22 st.push(o); 23 } 24 else { 25 if (o == ')') { 26 if (st.top() == '(')tmp++, st.pop(); 27 else ans = max(ans, tmp), tmp = 0, st.push(o); 28 } 29 else if (o == '}') { 30 if (st.top() == '{ ')tmp++, st.pop(); 31 else ans = max(ans, tmp), tmp = 0, st.push(o); 32 } 33 else if (o == ']') { 34 if (st.top() == '[')tmp++, st.pop(); 35 else ans = max(ans, tmp), tmp = 0, st.push(o); 36 } 37 else st.push(o); 38 } 39 } 40 ans = max(ans, tmp); 41 printf("%d\n", 2 * ans); 42 return 0; 43 }
POJ 1964 (二维单调栈)
题意:
给出一个格子矩阵,格子分为两种——可用和不可用。要求在画一个矩形,矩形所覆盖的格子全都是可用的,并且要求矩形的面积最大。
解法:
先处理每一行上面的‘F’最大高度,之后每一行跑一个单调栈即可;
1 #include2 #include 3 #include 4 #define REP(i, a, b) for(int i = (a); i <= (b); i++) 5 #define MEM(a,x) memset(a,x,sizeof(a)) 6 #define LL long long 7 #define MOD ((int)1000000007) 8 #define MAXN 2000+5 9 const int INF = 2147483647; 10 const int LLINF = 9223372036854775807; 11 using namespace std; 12 int h[MAXN][MAXN]; 13 int sta_h[MAXN], sta_p[MAXN]; 14 char ch[5]; 15 16 int main() { 17 int T; scanf("%d", &T); 18 while (T--) { 19 int n, m; scanf("%d%d", &n, &m); 20 REP(i, 1, n) { 21 REP(j, 1, m) { 22 scanf("%s", ch); 23 if (ch[0] == 'F')h[i][j] = h[i - 1][j] + 1; 24 else h[i][j] = 0; 25 } 26 h[i][m + 1] = 0; 27 } 28 29 int ans = 0; 30 REP(i, 1, n) { 31 int top = 0, mx = 0; 32 REP(j, 1, m + 1) { 33 int mnp = j; 34 while (top > 0 && h[i][j] <= sta_h[top]) { 35 mx = max(mx, (j - sta_p[top])*sta_h[top]); 36 mnp = min(mnp, sta_p[top]); 37 top--; 38 } 39 top++; sta_h[top] = h[i][j]; sta_p[top] = mnp; 40 } 41 ans = max(ans, mx); 42 } 43 printf("%d\n", ans * 3); 44 } 45 return 0; 46 }
POJ 2823 (单调队列)
题意:
给你一组数字,滚动窗口,每次只能看见几个数字,(每次左边出去一个,右边进来一个)求每次的最大数字和最小数字
解法:
这道题用G++的话会T,C++可以A掉;
首先肯定是单调队列求解,这个没有问题的;
这个队列里面存的元素是初始序列元素的下标;
1。我们一直注意的是,这两个队列的队首元素,如果这两个队列的队首元素超出了范围,那么肯定应该删掉,这个没有问题。
2。再次,对于单调队列,每次插入一个元素的时候,对于递减队列,所有的小于等于插入元素的元素都没有保留的必要, 因为滑窗已经划到了这里,反正我们一直要最大的,所以就全部删掉,同时又有第一个的性质的保证,可以使队首元素一直在框内;
递减队列同上。
很好的思路。
1 #include2 #include 3 #define REP(i, a, b) for(int i = (a); i < (b); i++) 4 #define MEM(a,x) memset(a,x,sizeof(a)) 5 #define LL long long 6 #define MOD ((int)1000000007) 7 #define MAXN 1000000+5 8 const int INF = 2147483647; 9 const LL LLINF = 9223372036854775807; 10 using namespace std; 11 int maxq[MAXN], minq[MAXN], num[MAXN]; 12 int maxans[MAXN], minans[MAXN]; 13 14 int main() { 15 int n, k; 16 while (scanf("%d%d", &n, &k) != EOF) { 17 int maxhead = 0, minhead = 0, maxtail = 0, mintail = 0; 18 REP(i, 0, n) { 19 if (maxhead < maxtail&&maxq[maxhead] <= i - k)maxhead++; 20 if (minhead < mintail&&minq[minhead] <= i - k)minhead++; 21 22 scanf("%d", &num[i]); 23 while (maxhead < maxtail&&num[maxq[maxtail - 1]] <= num[i]){maxtail--;} 24 maxtail++; 25 maxq[maxtail - 1] = i; 26 while (minhead < mintail&&num[minq[mintail - 1]] >= num[i]){mintail--;} 27 mintail++; 28 minq[mintail - 1] = i; 29 maxans[i] = num[maxq[maxhead]]; 30 minans[i] = num[minq[minhead]]; 31 } 32 REP(i, k - 1, n)printf("%d ", minans[i]);puts(""); 33 REP(i, k - 1, n)printf("%d ", maxans[i]);puts(""); 34 } 35 return 0; 36 }
BZOJ 2462 (二维字符串hash)
题意:
给出一个m*n的由01组成的矩阵,下面有q个询问,查询矩阵中存不存在大小为k*l的子矩阵。
解法:
二维的hash,本质是和一维一样的差分编码,我们对所有的大小为a*b的矩阵进行编码,
之后hash表查询一下就行了
1 #include2 #include 3 #define REP(i, a, b) for(int i = (a); i <= (b); i++) 4 #define MEM(a,x) memset(a,x,sizeof(a)) 5 #define LL long long 6 #define MOD ((int)10100) 7 #define MAXN 1100 8 const int INF = 2147483647; 9 const LL LLINF = 9223372036854775807; 10 using namespace std; 11 const int BASE1 = 10016957; 12 const int BASE2 = 10016959; 13 struct node { 14 unsigned num; 15 int next; 16 }table[MAXN*MAXN]; 17 unsigned int sum[MAXN][MAXN], power1[MAXN], power2[MAXN]; 18 int hash_table[MOD], tot; 19 20 void Hash(unsigned int x) { 21 int i, pos = x%MOD; 22 table[++tot].num = x; 23 table[tot].next = hash_table[pos]; 24 hash_table[pos] = tot; 25 } 26 27 bool Get_Hash(unsigned int x) { 28 int i, pos = x%MOD; 29 for(i = hash_table[pos]; i; i = table[i].next) { 30 if (table[i].num == x) 31 return true; 32 } 33 return false; 34 } 35 36 int main() { 37 power1[0] = power2[0] = 1; 38 REP(i, 1, MAXN - 1) { 39 power1[i] = power1[i - 1] * BASE1; 40 power2[i] = power2[i - 1] * BASE2; 41 } 42 43 int m, n, a, b; 44 scanf("%d%d%d%d", &m, &n, &a, &b); 45 REP(i, 1, m)REP(j, 1, n)scanf("%1d", &sum[i][j]); 46 REP(i, 1, m)REP(j, 1, n)sum[i][j] += sum[i - 1][j] * BASE1; 47 REP(i, 1, m)REP(j, 1, n)sum[i][j] += sum[i][j - 1] * BASE2; 48 49 //求一个矩阵的hash 50 REP(i, a, m)REP(j, b, n) { 51 unsigned int temp = sum[i][j] 52 - sum[i - a][j] * power1[a] 53 - sum[i][j - b] * power2[b] 54 + sum[i - a][j - b] * power1[a] * power2[b]; 55 Hash(temp); 56 } 57 58 int q; scanf("%d", &q); 59 while (q--) { 60 REP(i, 1, a)REP(j, 1, b)scanf("%1d", &sum[i][j]); 61 REP(i, 1, a)REP(j, 1, b)sum[i][j] += sum[i - 1][j] * BASE1; 62 REP(i, 1, a)REP(j, 1, b)sum[i][j] += sum[i][j - 1] * BASE2; 63 unsigned int temp = sum[a][b]; 64 if (Get_Hash(temp))puts("1"); 65 else puts("0"); 66 } 67 return 0; 68 }
CH1807 (字符串最小表示法)
题意:
求两个字符串的每个字符移位后是否能一样,如果可以的话输出其最小表示
解法:
先求两个字符串的最小同构,之后看他们两个的最小同构一样不一样即可
1 #include2 #include 3 #include 4 #define REP(i, a, b) for(int i = (a); i < (b); i++) 5 #define MEM(a,x) memset(a,x,sizeof(a)) 6 #define LL long long 7 #define MOD ((int)1000000007) 8 #define MAXN 1000000+5 9 const int INF = 2147483647; 10 const LL LLINF = 9223372036854775807; 11 using namespace std; 12 char s1[2*MAXN], s2[2*MAXN]; 13 14 int min_rep(char *s) { 15 int n = strlen(s+1); 16 for (int i = 1; i <= n; i++)s[n + i] = s[i]; 17 int i = 1, j = 2, k; 18 while (i <= n&&j <= n) { 19 for (k = 0; k <= n&&s[i + k] == s[j + k]; k++) {} 20 if (k == n)break; 21 if (s[i + k] > s[j + k]) { 22 i = i + k + 1; 23 if (i == j)i++; 24 } 25 else { 26 j = j + k + 1; 27 if (i == j)j++; 28 } 29 } 30 int ans = min(i, j); 31 return ans; 32 } 33 34 int main() { 35 scanf("%s%s", s1+1, s2+1); 36 int pos1 = min_rep(s1); 37 int pos2 = min_rep(s2); 38 if (strlen(s1) == strlen(s2)) { 39 int len = strlen(s1+1); 40 int i = pos1, j = pos2, flag = 1; 41 for (int k = 0; k < len/2; k++) { 42 if (s1[i] == s2[j]) { i++; j++; } 43 else { flag = 0; break; } 44 } 45 if (flag) { 46 puts("Yes"); 47 for (int i = pos1, k = 0; k < len/2; k++, i++) { 48 printf("%c", s1[i]); 49 } 50 } 51 else { 52 puts("No"); 53 } 54 } 55 else { 56 puts("No"); 57 } 58 return 0; 59 }
POJ 2185 (二维KMP)
题意:
给出一个二维的字符矩阵,问最小的可覆盖子矩阵大小。
解法:
二维的KMP,求一个最大矩阵的最小覆盖矩阵;
求出每一行的最小循环节,求其最小公倍数即是我们要的宽度;
每一列做同样的处理即可;
好用的板子hhh,要学会改;
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include <string> 8 #include 9 #include 10 #include 11 #include <set> 12 #include
CH1809 (EX-KMP)
题意:
阿轩是一个勤学好问的同学,他向你提出了Q个问题:在每个问题中,他给定你一个整数x,请你告诉他有多少个位置,满足“字符串A从该位置开始的后缀子串”与B匹配的长度恰好为x。
解法:
扩展KMP,求出a串的所有后缀与b串匹配的长度。 由于题目要求的特殊性,求从x位置开始与b串匹配长度为x的字符串数量,因此我们先遍历一遍之后累加,之后对于每个询问直接输出答案即可; 否则会T
1 #include2 #include 3 #define REP(i, a, b) for(int i = (a); i < (b); i++) 4 #define MEM(a,x) memset(a,x,sizeof(a)) 5 #define LL long long 6 #define MOD ((int)1000000007) 7 #define MAXN 200000+5 8 const int INF = 2147483647; 9 const LL LLINF = 9223372036854775807; 10 using namespace std; 11 char a[MAXN], b[MAXN]; 12 int n, m, cnt[MAXN]; 13 int Next[MAXN], ex[MAXN]; //ex数组即为extend数组 14 15 //扩展KMP 16 void GETNEXT(char *str) //预处理计算next数组 17 { 18 int i = 0, j, po, len = strlen(str); 19 Next[0] = len;//初始化Next[0] 20 while (str[i] == str[i + 1] && i + 1 < len)//计算Next[1] 21 i++; 22 Next[1] = i; 23 po = 1;//初始化po的位置 24 for (i = 2; i < len; i++) 25 { 26 if (Next[i - po] + i < Next[po] + po)//第一种情况,可以直接得到Next[i]的值 27 Next[i] = Next[i - po]; 28 else//第二种情况,要继续匹配才能得到Next[i]的值 29 { 30 j = Next[po] + po - i; 31 if (j < 0)j = 0;//如果i>po+Next[po],则要从头开始匹配 32 while (i + j < len&&str[j] == str[j + i])//计算Next[i] 33 j++; 34 Next[i] = j; 35 po = i;//更新po的位置 36 } 37 } 38 } 39 40 //计算extend数组 41 void EXKMP(char *s1, char *s2) 42 { 43 int i = 0, j, po, len = strlen(s1), l2 = strlen(s2); 44 GETNEXT(s2);//计算子串的next数组 45 while (s1[i] == s2[i] && i < l2&&i < len)//计算ex[0] 46 i++; 47 ex[0] = i; 48 po = 0;//初始化po的位置 49 for (i = 1; i < len; i++) 50 { 51 if (Next[i - po] + i < ex[po] + po)//第一种情况,直接可以得到ex[i]的值 52 ex[i] = Next[i - po]; 53 else//第二种情况,要继续匹配才能得到ex[i]的值 54 { 55 j = ex[po] + po - i; 56 if (j < 0)j = 0;//如果i>ex[po]+po则要从头开始匹配 57 while (i + j < len&&j < l2&&s1[j + i] == s2[j])//计算ex[i] 58 j++; 59 ex[i] = j; 60 po = i;//更新po的位置 61 } 62 } 63 } 64 65 int main() { 66 int n, m, q; 67 scanf("%d%d%d", &n, &m, &q); 68 scanf("%s%s", a, b); 69 EXKMP(a, b); 70 REP(i, 0, n) cnt[ex[i]]++; 71 while (q--) { 72 int o; scanf("%d", &o); 73 printf("%d\n", cnt[o]); 74 } 75 return 0; 76 }
POJ 3630 (前缀树)
题意:
如果给的字符串都不是其他串的前缀,输出YES,否则输出NO
解法:
简单的trie树,注意怎样判断即可
tot也要重置!
1 #include2 #include 3 #include 4 #include 5 #define REP(i, a, b) for(int i = (a); i < (b); i++) 6 #define MEM(a,x) memset(a,x,sizeof(a)) 7 #define LL long long 8 #define MOD ((int)1000000007) 9 #define MAXN 500000+100 10 const int INF = 2147483647; 11 const LL LLINF = 9223372036854775807; 12 using namespace std; 13 char s[MAXN]; 14 int trie[MAXN][15], tot = 1; 15 int _end[MAXN]; 16 17 bool insert(char *str) { 18 int len = strlen(str), p = 1; 19 bool flag = 0; 20 REP(k, 0, len) { 21 int ch = str[k] - '0'; 22 if (trie[p][ch] == 0) { 23 flag = 1; 24 _end[p] = 0; 25 trie[p][ch] = ++tot; 26 } 27 p = trie[p][ch]; 28 if (_end[p]) return 0; 29 } 30 _end[p] = true; 31 return flag; 32 } 33 34 int main() { 35 int T; scanf("%d", &T); 36 while (T--) { 37 tot = 1; 38 MEM(trie, 0); MEM(_end, 0); 39 int n; scanf("%d", &n); 40 bool flag = 1; 41 REP(i, 0, n) { 42 scanf("%s", s); 43 if (flag) flag = insert(s); 44 } 45 if (flag) puts("YES"); 46 else puts("NO"); 47 } 48 return 0; 49 }
POJ 1442 (对顶堆,优先队列)
题意:
有n次操作,每次往一个小根堆中插入一个数,有m次询问,询问在mi次插入后,小根堆种排列第i小的数
解法:
用一个大根堆和一个小根堆维护一个递减的序列,大根堆用来表示前0~i-1个最小的元素里面最大的元素,
小根堆用来表示剩下的元素里面最大的元素,所哟,当大根堆的堆顶大于小根堆的堆顶到时候我就应该将他们交换位置,
进而维护一个单调递增的序列,由于题目要求每次输出第i小的元素,因此我们每次输出完之后都往将小根堆的堆顶插入到大根堆
之中,这一步也就是所谓的后移操作,之后每次询问我们输出小根堆的堆顶即可。
1 #include2 #include 3 #include 4 #include 5 #define REP(i, a, b) for(int i = (a); i < (b); i++) 6 #define MEM(a,x) memset(a,x,sizeof(a)) 7 #define LL long long 8 #define MOD ((int)1000000007) 9 #define MAXN 30000+5 10 const int INF = 2147483647; 11 const LL LLINF = 9223372036854775807; 12 using namespace std; 13 priority_queue <int, vector<int>, less<int> > p;//从大到小 14 priority_queue <int, vector<int>, greater<int> > q;//从小到大 15 int num[MAXN]; 16 17 int main() { 18 int n, m; scanf("%d%d", &n, &m); 19 REP(i, 1, n+1) scanf("%d", &num[i]); 20 int j = 1; 21 REP(i, 0, m) { 22 int o; scanf("%d", &o); 23 while (j <= o) { 24 q.push(num[j]); 25 if (!p.empty() && q.top() < p.top()) { 26 int mini = q.top(), maxi = p.top(); 27 q.pop(); p.pop(); 28 q.push(maxi); p.push(mini); 29 } 30 j++; 31 } 32 printf("%d\n", q.top()); 33 p.push(q.top()); q.pop(); 34 } 35 return 0; 36 }