contents:
0x11栈
链接: link.
表达式求值
链接: link.
POJ2559
视野总和
int FieldSum(vector<int>& v)
{
v.push_back(INT_MAX);/这里可以理解为需要一个无限高的人挡住栈中的人,不然栈中元素最后无法完全出栈
stack<int> st;
int sum = 0;
for (int i = 0; i < (int)v.size(); i++)
{
if (st.empty() || v[st.top()] > v[i])//小于栈顶元素入栈
{
st.push(i);
}
else
{
while (!st.empty() && v[st.top()] <= v[i])
{
int top = st.top();//取出栈顶元素
st.pop();
sum += (i - top - 1);//这里需要多减一个1
}
st.push(i);
}
}
return sum;
}
柱状图中的最大矩形
int largestRectangleArea(vector<int>& heights) {
heights.push_back(-1);//同理,我们希望栈中所有数据出栈,所以给数组最后添加一个负数
stack<int> st;
int ret = 0, top;
for (int i = 0; i < heights.size(); i++)
{
if (st.empty() || heights[st.top()] <= heights[i])
{
st.push(i);
}
else
{
while (!st.empty() && heights[st.top()] > heights[i])
{
top = st.top();
st.pop();
//i-top指的是当前矩形的宽度,heights[top]就是当前的高度
//再次强调栈中现在为单调递增
int tmp = (i - top)*heights[top];
if (tmp > ret)
ret = tmp;
}
st.push(top);
heights[top] = heights[i];
}
}
return ret;
}
最大区间
int GetMaxSequence(vector<int> &v)
{
stack<int> st;
vector<int> vs(v.size() + 1);
vs[0] = 0;
for (int i = 1; i < vs.size(); i++)
vs[i] = vs[i - 1] + v[i - 1];
v.push_back(-1);
int top, start, end, ret = 0;
for (int i = 0; i < v.size(); i++)
{
if (st.empty() || v[st.top()] <= v[i])
{
st.push(i);
}
else
{
while (!st.empty() && v[st.top()] > v[i])
{
top = st.top();
st.pop();
int tmp = vs[i] - vs[top];
tmp = tmp * v[top];
if (tmp > ret)
{
ret = tmp;
start = top + 1;
end = i;
}
}
st.push(top);
v[top] =v[i]; //与第二题相同的道理,将当前数据的更改最左
//的top下标,防止出现比当前数据更小的数据
//这句在这道题里真的超级难理解,但是只
//要你有耐心相信你可以理解的
}
}
return ret;
}
0x12队列
Team queue poj2259
链接: link.
蚯蚓 CH202
链接: link.
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define p u/v
#define fir(i,a,b) for(ll i=a;i<=b;i++)
const int N=1e7+10;
queue<ll> p1,p2,p3;
ll n,m,q,u,v,t,a[N],data;
int cmp(int a,int b)
{
return a>b;
}
int calc(ll t)
{
ll x=-1,a=-1,b=-1,c=-1;
if (!p1.empty())
a=p1.front()+t*q;
if (!p2.empty())
b=p2.front()+t*q;
if (!p3.empty())
c=p3.front()+t*q;
x=max(a,max(b,c));
if (x==a)
p1.pop();
else
if (x==b)
p2.pop();
else
if (x==c)
p3.pop();
return x;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m>>q>>u>>v>>t;
fir(i,1,n)
cin>>a[i];
sort(a+1,a+1+n,cmp);
fir(i,1,n)
p1.push(a[i]);
fir(i,1,m)
{
ll x=calc(i-1);
if (!(i%t))
cout<<x<<" ";
ll now1=x*p;
ll now2=x-now1;
p2.push(now1-i*q);
p3.push(now2-i*q);
}
cout<<endl;
fir(i,1,(n+m))
{
ll x=calc(m);
if (i%t==0)
cout<<x<<" ";
}
return 0;
}
双端队列 ACW134
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define pii pair<int, int>
#define psi pair<string, int>
#define ull unsigned ll
#define pb push_back
#define mp make_pair
#define X first
#define Y second
#define ld long double
const int N = 2E5 + 7;
#define INF ~0ULL
int n;
pii arr[N];
int mn[N], mx[N];
// bool cmp(pii a, pii b)
// {
// if (a.X == b.X)
// return a.Y < b.Y;
// return a.X < b.X;
// }
int main()
{
scanf("%d", &n);
for (register int i = 1; i <= n; i++)
scanf("%d", &arr[i].X), arr[i].Y = i;
sort(arr + 1, arr + 1 + n);
mn[1] = arr[1].Y;
int cnt = 1;
for (int i = 2; i <= n; i++)
{
if (arr[i].X != arr[i - 1].X)
{
mx[cnt] = arr[i - 1].Y;
mn[++cnt] = arr[i].Y;
}
}
mx[cnt] = arr[n].Y;
int ans = 1;
bool rs = 0;
for (int i = 2; i <= cnt; i++)
{
if (rs == 1) //上升中
{
if (mn[i] < mx[i - 1])//上升不了了
rs = 0, ++ans;
}
else
{
if (mx[i] > mn[i - 1])//下降不了
rs = 1;
}
}
printf("%d\n", ans);
return 0;
}
最大子序和 CH1201
滑动窗口m大小的区间内的和最大
首先前缀和一下子
枚举右端点i
当i确定的时候
问题就变成了
找到一个左端点j,其中j属于[i-m,i-1] 并且s[j] 最小
随便比较一下子j和k,如果k<j<i;
并且s[k]>=s[j]
那么对于所有大于等于i的右端点,k永远不会成为最优选择,这是
因为不但s[k],不小于s[j],而且j离i更近,长度更不容易超过m
,即j的生存能力比k更强,所以当j出现后,k就完全是一个无用的位置
可能成为最优选择的策略集合一定是一个“
下标位置递增,对应的前缀和s的值也递增”的序列
我们可以用一个队列保存这个序列
随着右端点从前先后扫描,我们对每个i执行以下三个步骤
1、判断对头决策与i的距离是否超出了m的范围,若超出则出队
2、此时队头就是右端点为 i 时,左端点j的最佳选择
3、不断删除队尾决策,直到队尾对应的s值小于s[i],然后把i作为一个新的决策入队
int l = 1, r = 1;
q[1] = 0;
for (int i = 1; i <= n; i++)
{
while (l <= r && q[l] < i - m)
l++; // step1
ans = max(ans, sum[i] - sum[q[l]]); // step2
while (l <= r && sum[q[r]] >= sum[i])
r--; // step3
q[++r] = i;
}
因为每个元素至多入队一次,出队一次,所以整个算法
的时间复杂度就是0n
它的思想就是在决策集合队列中及时排除一定不是
最优解的选择,单调队列是优化动态规划的一个重要手段
0x13链表与邻接表
0x14Hash
CH1401
字符串hash
F[i]表示前缀子串s[1~i] 的hash值,有F[i] = F[i-1] *131 + (S[i]-“a”+1)。
于是我们可以得到任意区间[l,r]的hash值为F[r]-F[l-1] *131(r-l+1)
当两个区间的Hash值相同时,我们就认为对应的两个子串相等,整个算法的时间复杂度为O(s+q)
char s[1000010];
ull f[1000010], p[1000010];
int main()
{
scanf("%s", s + 1);
int n = strlen(s + 1), q;
cin >> q;
p[0] = 1; // 131^0
for (int i = 1; i <= n; i++)
{
f[i] = f[i - 1] * 131 + (s[i] - 'a' + 1); // hash of 1~i
p[i] = p[i - 1] * 131; // 131^i
}
for (int i = 1; i <= q; i++)
{
int l1, l2, r1, r2;
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
if (f[r1] - f[l1 - 1] * p[r1 - l1 + 1] == f[r2] - f[l2 - 1] * p[r2 - l2 + 1])
puts("YES");
else
puts("NO");
}
}
POJ3974 Palindrome
回文子串 hash+二分答案做法详解
链接: link.
manacher
链接: 漫画理解.
链接: bilibili.
链接: CSDN.
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define pii pair<int,int>
#define psi pair<string,int>
#define ull unsigned ll
#define pb push_back
#define mp make_pair
#define X first
#define Y second
#define ld long double
const int N = 2E7 + 7;
#define INF ~0ULL
char arr[N];
int p[N];
string str;
int len,pos,r,ans;
#define check(x) ((x>='a'&&x<='z') \
||(x>='A'&&x<='Z') || (x>='0'&&x<='9'))
string read() {
string s;
char ch;
ch = getchar();
while (check(ch)) {
s += ch;
ch = getchar();
}
return s;
}
void addchar()
{
int i = 0, j = 0;
arr[j++] = '@';
while(j < 2 * len)
{
arr[j++] = '#';
arr[j++] = str[i++];
}
arr[j] = '#';
len = 2 * len + 2; ///len更新成2 * len + 2
}
void Manacher()
{
pos = r = ans = 0;
for(int i = 0; i < len; i++)
{
if(i < r)
p[i] = min(p[2 * pos - i], r - i);
else
p[i] = 1;
while(arr[i + p[i]] == arr[i - p[i]]) ///从当前位置当前半径向外扩张
p[i]++;
if(i + p[i] > r) ///更新右界
{
r = i + p[i];
pos = i;
}
ans = max(ans, p[i] - 1);
}
}
int main()
{
str = read();
len = str.length();
addchar();
Manacher();
cout<<ans<<endl;
}
0x15字符串
最小表示法
链接: link.
0x16Trie
前缀统计
链接: link.
最大异或和
链接: link.
poj3764
链接: link.
0x17二叉堆
超市
https://www.acwing.com/problem/content/147/
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define pii pair<int, int>
#define psi pair<string, int>
#define ull unsigned ll
#define pb push_back
#define mp make_pair
#define X first
#define Y second
#define ld long double
const int N = 1E5 + 7;
#define INF ~0ULL
int n;
pii arr[N]; // id,value;
priority_queue<int,vector<int>,greater<int> > Q;//value
ll ans;
int main()
{
while (cin >> n)
{ ans=0;
for (int i = 1; i <= n; i++)
{
cin >> arr[i].Y >> arr[i].X;
}
sort(arr + 1, arr + 1 + n);
Q.push(arr[1].Y);
int cnt = 1;
for (int i = 2; i <= n; i++)
{
if(cnt<arr[i].X){
Q.push(arr[i].Y);
cnt++;
}else{
int top = Q.top();
if(arr[i].Y>top){
Q.pop();
Q.push(arr[i].Y);
}
}
}
while(!Q.empty()){
int top=Q.top();
Q.pop();
ans+=top;
}
cout<<ans<<endl;
}
}
sequence
链接: link.
数据备份
链接: link.
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pli pair<ll, int>
const int N = 1e5 + 7;
int n, k;
int l[N], r[N];
ll d[N];
void delete_node(int p)
{//待删除节点p
r[l[p]] = r[p];
l[r[p]] = l[p];
}
int main()
{
cin >> n >> k;
for (int i = 0; i < n; i++)
cin >> d[i];
for (int i = n - 1; ~i; i--)
d[i] -= d[i - 1];
set<pli> S;
d[0] = d[n] = 1e15;
for (int i = 0; i <= n; i++)
{
l[i] = i - 1;
r[i] = i + 1;
if (i >= 1 && i < n)
S.insert({d[i], i});
}
ll ans = 0;
while (k--)
{
auto it = S.begin();
ll v = it->first;
int p = it->second, left = l[p], right = r[p];
S.erase(it);
S.erase({d[left],left}),S.erase({d[right],right});
delete_node(left), delete_node(right);
ans +=v;
d[p] =d[left]+d[right]-d[p];
S.insert({d[p],p});
}
cout<<ans<<endl;
return 0;
}
合并果子
链接: link.