前言
传送门 :
翻译水瓶真的不够,太影响做题了
勉强水了个铜,这场并没有多少体验
需要加强训练的地方 :
- 优先队列 处理区间问题
- Tarjan缩点
- 翻译水瓶
- 二分
- 3小时的阀座训练
- CF的训练
A.基础二分
题目意思就是摘气球,前面摘了后面就不可以摘了
求一遍二分就行
心路历程 :
这题虽然说一开始看完之后就知道是二分,但是还是做了一个小时我才A出来,
实在是太废了
1.实在是太紧张了
2.对于lower_bound和upper_bount以及greater<int> 不是非常熟练
3.对于手写二分 完全不熟悉
CODE
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i];
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
mp[a[i]] = i;
for(int i=1;i<=m;i++)
cin>>h[i];
sort(h+1,h+1+m);
for(int i=1;i<=n;i++)
{
int d = upper_bound(h+1,h+1+m,a[i]) - h;
d--;
if(d >= m)
{
d = m;
}
ans[i] = d;
temp[i] = d;
ans[i] -= temp[i-1];
mp[a[i]]=ans[i];
if(ans[i]<0)
ans[i] = 0;
}
for(int i=1;i<=n;i++)
{
cout<<mp[b[i]]<<endl;
}
}
B.基础线性DP
看完之后 就是很裸的一个线性DP问题
我们可以 反着处理 因此状态转移方程就很简单了
f [ i ] = m a x ( f [ i − d − 1 ] + w [ i ] , f [ i − 1 ] ) f[i] = max(f[i-d-1]+w[i],f[i-1]) f[i]=max(f[i−d−1]+w[i],f[i−1])
心路历程 :
虽然一开始就可以直接想到状态转移方程
但是还是A了一个小时才做出来
- 因为A题太紧张,脑袋涣散了
- 不小心写错一个变量导致 O n n n 做法出错,否定自己,然后写了一个O n 2 n^2 n2
- 结果优化了好久的 O n 2 O n^2 On2 最后才被队友提醒
CODE
void solve()
{
int n,d,m;
cin>>n>>d>>m;
for(int i=1;i<=n;i++)
{
cin>>w[i];
f[i] = 0;
}
reverse(w+1,w+1+n);
reverse(f+1,f+1+n);
for(int i=1;i<=n;i++)
{
if(w[i]>m)
{
if(i-d-1>=1)
f[i] = max(f[i-1],f[i-d-1]+w[i]);
else
f[i] = max(f[i-1],w[i]);
continue;
}
f[i] = f[i-1]+w[i];
}
cout<<f[n]<<endl;
}
C.Tarjan缩点+拓扑
题意
不懂,完全不懂
CODE
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e6+10, M = 5e4+10;
vector<int> g[N];
stack<int> stk;
int in_stk[N];
int scc_cnt;
int tempstamp;
int dfn[N],low[N];
int id[N],size_scc[N];
int in[N];
int n,m;
///dfn[i] 遍历到i 的时间戳
///low[i] 从u开始走,能遍历到的最小时间戳
///如果u是其所在的强连通分量最高点 那么 dfn[u] = low[u]
/**
如果 i 和 j 不在同一个 scc(强连通分量) 当中 加 id[i] ->id[j]
**/
void tarjan(int u)
{
dfn[u] = low[u] = ++tempstamp;
stk.push(u);
in_stk[u] = 1;
for(auto x:g[u])
{
if(!dfn[x])
{
tarjan(x);
low[u] = min(low[u],low[x]);
}
else if(in_stk[x])
{
low[u] = min(low[u],dfn[x]);
}
}
if(dfn[u] ==low[u])
{
++ scc_cnt;
int y;
do
{
y = stk.top();
stk.pop();
in_stk[y] = 0 ;
id[y] = scc_cnt;
size_scc[scc_cnt] ++;
///对于每一个scc连通块的编号
}
while(y!=u);
}
}
vector<int> g2[N];
int in2[N];
ll dist[N];
void topsort()
{
queue<int> q;
for(int i=1; i<=scc_cnt; i++)
if(!in2[i])
{
q.push(i);
dist[i] = size_scc[i];
}
while(!q.empty())
{
int t= q.front();
q.pop();
for(auto x :g2[t])
{
dist[x] = max(dist[x],dist[t]+size_scc[x]);
--in2[x];
if(!in2[x])
q.push(x);
}
}
}
void solve()
{
cin>>n>>m;
while(m -- )
{
int a,b;
cin>>a>>b;
g[a].push_back(b);
}
for(int i=1; i<=n; i++)
{
if(!dfn[i])
{
tarjan(i);
}
}
///建立DAG 跑拓扑排序
for(int i=1; i<=n; i++)
{
for(auto x : g[i])
{
int a = id[i];
int b = id[x];
if(a!=b)
{
g2[a].push_back(b);
in2[b]++;
}
}
}
topsort();
ll ans = 0;
for(int i=1; i<=scc_cnt; i++)
ans= max(ans,dist[i]);
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
///int t;cin>>t;while(t -- )
solve();
return 0;
}
D.优先队列处理区间
前言
属于是 知识盲区了,或者是 虽然会用但是不会这么用
思路
对于这题无非就是 从一个大区间开始减,左边右边都减
然后我们可以用 m a p map map 和 大根堆进行 操作就行了
具体看代码
CODE
struct node
{
int l,r;
ll sum;
friend bool operator< (node x,node y )
{
return x.sum<y.sum;
}
};
ll a[N],sum;
void solve()
{
cin>>n>>m;
for(int i = 1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
priority_queue<node> q;
q.push({1,n,sum});
mp[{1,n}] = 1;
while(m)
{
auto t = q.top();
q.pop();
m --;
cout<<t.sum<<" ";
if(t.l != t.r)
{
if(!mp[{t.l +1 ,t.r}])
q.push({t.l+1,t.r,t.sum - a[t.l]});
if(!mp[{t.l,t.r-1}])
q.push({t.l,t.r-1,t.sum-a[t.r]});
mp[{t.l+1,t.r}] = mp[{t.l,t.r-1}] = 1;
}
}
}
E.模拟?
前言
补完题,笑尿了…
大概CF div2D ? 或者 C
思路
观察范围 :
a
[
i
]
<
=
5000
a[i] <= 5000
a[i]<=5000 又
∑
a
[
i
]
<
=
5000
\sum a[i] <= 5000
∑a[i]<=5000
这说明了什么呢,说明除0外,我们最多只有5000的值
也就是实际上 n < 1 e 5 n<1e5 n<1e5但是呢 n < = 5000 n<=5000 n<=5000
所以我们可以 O n 2 O n^2 On2的处理
我们使用 两个数组 处理非0的下标 然后我们 N 2 N^2 N2的求出答案即可
对于 0 0 0的数组,我们只需要在输出答案的时候输出 m a x n = m a x ( a , b ) maxn= max(a,b) maxn=max(a,b)就行了
CODE
void solve()
{
cin>>n;
int maxn = 0 ;
for(int i = 0 ;i<n;i++)
{
cin>>a[i];
if(a[i])
s1.pb(i), maxn = max(maxn,a[i]);
}
for(int i = 0;i<n;i++)
{
cin>>b[i];
if(b[i])
s2.pb(i), maxn = max(maxn,b[i]);
}
for(int i=0;i<s1.size();i++)
for(int j=0;j<s2.size();j++)
{
int k = (s1[i]+s2[j])%n;
c[k] =max(c[k],a[s1[i]]+b[s2[j]]);
}
for(int i=0;i<n;i++)
cout<<max(c[i],maxn)<<" ";
}