Codeforces Round #620 (Div. 2)
题目链接 https://codeforces.com/contest/1313
A B C题直接跳过。
D题:
求最短的上升子序列,我们可以直接假设它是1~n从大到小排列,然后对于每两个大于号之间的小于号再翻转就行。
求最长的就反过来做一遍就行。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[300050],ans[300050];
char s[300050];
void solve(){
int head=0;
for(int i=1;i<=n;i++){
if (s[i]=='>'){
for(int j=head+1;j<=i;j++)
ans[j]=n-i+j-head;
head=i;
}
}
}
void work(){
cin>>n>>s+1;
s[n]='>';
solve();
for(int i=1;i<=n;i++)cout<<ans[i]<<"
"[i==n];
for(int i=1;i<n;i++)s[i]^='<'^'>';
solve();
for(int i=1;i<=n;i++)cout<<n+1-ans[i]<<"
"[i==n];
}
int main(){
int T;
cin>>T;
while(T--)work();
}
E题
这个题目,如果不加一条边,那么就是如果距离是(L,k=L+2x(x>=1))都可以,因为你可以在一条边来回走。
增加一条边,你如果走这条边偶数次,相当于没走。走奇数次,奇偶性都一样,所以我们就设置只走一次,那么只要求dist(x,a)+dist(y,b)和dist(x,b)+dist(y,a),令(L2=min(dist(x,a)+dist(y,b),dist(x,b),dist(y,a))+1 ; k=L2+2x)都可以。求举例就有用深度,(dist(x,y)=d[x]+d[y]-2*d[lca(x,y)]), 求lca就用倍增就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 200050
struct Edge{int x,y,s;};
struct Tree{
int n;
int fa[N][20],d[N];
int tot=0,last[N];
Edge edges[N<<1];
void addEdge(int x,int y){
edges[++tot]=Edge{x,y,last[x]};
last[x]=tot;
}
void readTree(){
cin>>n;
for(int i=1;i<=n-1;i++){
int x,y;
cin>>x>>y;
addEdge(x,y);
addEdge(y,x);
}
dfs(1);
build();
}
void dfs(int u){
d[u]=d[fa[u][0]]+1;
for(int i=last[u];i;i=edges[i].s){
Edge &e=edges[i];
if (fa[u][0]==e.y)continue;
fa[e.y][0]=u;
dfs(e.y);
}
}
void build(){
for(int i=1;i<=19;i++)
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
int lca(int x,int y){
if (d[x]<d[y])swap(x,y);
for(int i=19;i>=0;i--)
if (d[fa[x][i]]>=d[y]) x=fa[x][i];
if (x==y) return x;
for(int i=19;i>=0;i--)
if (fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
int dist(int x,int y){return d[x]+d[y]-2*d[lca(x,y)];}
void solve(){
bool flag=false;
int x,y,a,b,k;
cin>>x>>y>>a>>b>>k;
int t1=dist(a,b);
int t2=min(dist(a,x)+dist(b,y),dist(a,y)+dist(b,x))+1;
if (t1<=k && (k-t1)%2==0) flag=true;
if (t2<=k && (k-t2)%2==0) flag=true;
string s[]={"NO","YES"};
cout<<s[flag]<<endl;
}
}Tr;
int main(){
int T;
Tr.readTree();
cin>>T;
while(T--) Tr.solve();
}
F题:
F分成F1和F2。动规题,设(a[i][j])表示第i天j位置的动物数量,设表示第i天在j位置放下超相机,也就是记录
设(dp[i][j])表示第i天在j位置放下超相机,也就是记录$ a[i到i+1][j到j+k-1]$,
第(i)天照相机的位置可能会和第(i-1)照相机有动物重叠。
所以递推式就是:
(dp[i][j]=max(dp[i-1][h]-重叠部分)+sum_{x=i}^{i+1}sum_{y=j}^{j+k-1}a[x][y])
我们会发现,上一个照相机的摆放位置可以分成三种情况:
- ([1,j-k]): 没有重叠
- ([j-k+1,j+k-1]):有重叠
- ([j+k,m]): 没有重叠
方法一:
F1, (k)很小,没有重叠的部分只要取最大值就行了,这个很好做,从前扫一遍,从后扫一遍(dp[i-1])就行了,然后重叠部分一个一个判断.
时间复杂度(O(nmk))
方法二:
我们用一个新数组(c[h]=dp[i-1][h]-重合部分).
对于每个每个位置(j) ,都需要更新(c[i])数组,
从(j 到 j+1):
- (c[j-k+1 到 j]全部+a[i][j])
- (c[j+1到j+k]全部-a[i][j+k])
- 其余的不变
很明显只要改变两个区间,做两个区间加法就行,于是我们可以用线段树。
时间复杂度(O(nmlogm))
方法三:
就是怎么优化方法二,随之(j)的不断增大,我们发现,中间的重复的部分分成两个部分,左半部分重合部分越来越少,右半部分重合越来越多,并且右半部分重合到最多的时候就开始转入左半部分(因为重合部分越来越少)。
左半部分范围([j-k+1,j]),其实就是要求所有区间长度为(k)的最大值,这个就和滑动窗口那题很像。用单调队列,保持队列递减,元素为c[h],然后从(j到j+1),用一个变量(t)记录整个区间加了多少,如果后入队的,先把权值减去这个(t)。没有重合的部分还是按照方法一预处理就行。
右半部分也是一样,再开一个单调队列。
时间复杂度(O(nm)) (但是我的代码比线段树还慢,后来模仿std重写了一下,发现原来是没有关闭同步,qwq)
代码一 (O(nmk))
#include<bits/stdc++.h>
using namespace std;
#define N 60
#define M 30050
int n,m,k,ans;
int a[N][M],s[N][M];
int lmax[M],rmax[M];
int dp[M],newdp[M];
int main(){
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>a[i][j];
s[i][j]=a[i][j]+s[i][j-1];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
lmax[j]=max(lmax[j-1],dp[j]);
for(int j=m;j>=1;j--)
rmax[j]=max(rmax[j+1],dp[j]);
for(int j=1;j<=m-k+1;j++){
int val=s[i][j+k-1]-s[i][j-1]+s[i+1][j+k-1]-s[i+1][j-1];
newdp[j]=max(lmax[max(j-k,0)],rmax[min(j+k,m+1)])+val;
for(int h=max(j-k+1,1);h<=min(j+k-1,m);h++){
int r=min(j+k-1,h+k-1), l=max(j,h);
newdp[j]=max(newdp[j],val+dp[h]-s[i][r]+s[i][l-1]);
}
//cout<<j<<" "<<newdp[j]<<endl;
}
for(int i=1;i<=m;i++)dp[i]=newdp[i];
}
for(int i :dp)ans=max(ans,i);
cout<<ans;
}
代码二 线段树 (O(nmlog m))
#include<bits/stdc++.h>
using namespace std;
#define M 20050
#define N 60
int s[N][M],a[N][M];
int dp[M],newdp[M];
struct SegmentTree{
int L[M<<2],R[M<<2],Max[M<<2],J[M<<2];
int n;
SegmentTree (int n){
this->n=n;
build(1,1,n);
}
void push_up(int x){
if (L[x]==R[x])Max[x]=0;
else Max[x]=max(Max[x<<1],Max[x<<1|1]);
Max[x]+=J[x];
}
void push_down(int x){
J[x<<1]+=J[x];
J[x<<1|1]+=J[x];
J[x]=0;
push_up(x<<1);
push_up(x<<1|1);
}
void build(int x,int l,int r){
L[x]=l; R[x]=r; Max[x]=J[x]=0;
if (l==r)return;
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
}
void clear(){build(1,1,n);}
void add(int x,int l,int r,int p){
if (l>r)return;
if (l<=L[x] && R[x]<=r){
J[x]+=p;
push_up(x);
return;
}
int mid=(L[x]+R[x])>>1;
push_down(x);
if (l<=mid) add(x<<1,l,r,p);
if (mid<r) add(x<<1|1,l,r,p);
push_up(x);
}
int get(){return Max[1];}
};
int main(){
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int n,m,k,ans=0;
cin>>n>>m>>k;
SegmentTree Seg(m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>a[i][j];
s[i][j]=a[i][j]+s[i][j-1];
}
for(int i=1;i<=n;i++){
Seg.clear();
for(int j=1;j<=k;j++) Seg.add(1,j,j,dp[j]-s[i][k]+s[i][j-1]);
for(int j=k+1;j<=m;j++) Seg.add(1,j,j,dp[j]);
for(int j=1;j<=m-k+1;j++){
int val=s[i][j+k-1]-s[i][j-1]+s[i+1][j+k-1]-s[i+1][j-1];
newdp[j]=val;
newdp[j]=max(newdp[j],Seg.get()+val);
Seg.add(1,max(1,j-k+1),j,a[i][j]);
Seg.add(1,j+1,min(j+k,m),-a[i][j+k]);
}
for(int j=1;j<=m;j++)dp[j]=newdp[j];
}
for(int i: dp)ans=max(ans,i);
cout<<ans;
}
代码三 单调队列1 第一次写 (O(nm))
#include<bits/stdc++.h>
using namespace std;
int n,m,k,lmax,now1,now2,ans=0;
int a[55][30050],s[55][30050],dp[55][30050];
struct Point{int x,val;};
deque<Point>Q1,Q2,Q3;
void init(){
lmax=0;
now1=0;
now2=0;
while(!Q1.empty())Q1.pop_back();
while(!Q2.empty())Q2.pop_back();
while(!Q3.empty())Q3.pop_back();
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>a[i][j];
s[i][j]=a[i][j]+s[i][j-1];
}
for(int j=1;j<=m-k+1;j++){
dp[1][j]=s[1][j+k-1]-s[1][j-1]+s[2][j+k-1]-s[2][j-1];
ans=max(ans,dp[1][j]);
}
for(int i=2;i<=n;i++){
init();
Q1.push_back(Point{1,dp[i-1][1]-s[i][k]});
for(int j=2;j<=k;j++){
int val=dp[i-1][j]-s[i][k]+s[i][j-1];
while(!Q2.empty() && val>=Q2.back().val)Q2.pop_back();
Q2.push_back(Point{j,val});
}
for(int j=k+1;j<=m;j++){
while(!Q3.empty() && dp[i-1][j]>=Q3.back().val) Q3.pop_back();
Q3.push_back(Point{j,dp[i-1][j]});
}
Q3.push_back(Point{m+1,0});
for(int j=1;j<=m-k+1;j++){
if (j-k) lmax=max(lmax,dp[i-1][j-k]);
while(Q1.front().x<j-k+1)Q1.pop_front();
while(!Q2.empty() && Q2.front().x<j+1)Q2.pop_front();
while(Q3.front().x<j+k)Q3.pop_front();
int val1=Q1.front().val+now1;
if (!Q2.empty())val1=max(val1,Q2.front().val+now2);
int val2=max(lmax,Q3.front().val);
int val3=s[i][j+k-1]-s[i][j-1]+s[i+1][j+k-1]-s[i+1][j-1];
dp[i][j]=max(val1,val2)+val3;
ans=max(ans,dp[i][j]);
//cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
now1+=a[i][j];
int val=dp[i-1][j+1]-s[i][j+k]+s[i][j]-now1;
while(!Q1.empty() && val>Q1.back().val)Q1.pop_back();
Q1.push_back(Point{j+1,val});
now2-=a[i][j+k];
val=dp[i-1][j+k]-s[i][j+k]+s[i][j+k-1]-now2;
while(!Q2.empty() && val>Q2.back().val)Q2.pop_back();
Q2.push_back(Point{j+k,val});
}
}
cout<<ans;
}
代码四 单调队列2 模仿std重写 (O(nm))
#include<bits/stdc++.h>
using namespace std;
using vi=vector<int>;
using vvi=vector<vi>;
using pii=pair<int,int>;
const int MAX_N = 50;
const int MAX_M = 20000;
const int MAX_K = 20000;
int n,m,k;
void calc(vi &a,vi &s, vi &dp, vi &newdp){
deque<pii> Q;
int now=0;
for(int i=1;i<=m-k+1;i++){
while(!Q.empty() && Q.front().first<i-k+1)Q.pop_front();
now+=a[i-1];
int val=dp[i]-s[i+k-1]+s[i-1]-now;
while(!Q.empty() && Q.back().second<=val)Q.pop_back();
Q.push_back({i,val});
newdp[i]=Q.front().second+now;
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>k;
vvi init_vvi(n+5,vi(m+5,0));
vi init_vi(m+5,0);
vi dp,dpl,dpr,newdpl,newdpr,lmax,rmax;
vvi a,s,a_rev,s_rev;
dp=dpl=dpr=newdpl=newdpr=lmax=rmax=init_vi;
a=s=a_rev=s_rev=init_vvi;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>a[i][j];
s[i][j]=s[i][j-1]+a[i][j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
a_rev[i][j]=a[i][m+1-j];
s_rev[i][j]=s_rev[i][j-1]+a_rev[i][j];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
lmax[j]=max(lmax[j-1],dp[j]);
for(int j=m;j>=1;j--)
rmax[j]=max(rmax[j+1],dp[j]);
calc(a[i],s[i],dpl,newdpl);
calc(a_rev[i],s_rev[i],dpr,newdpr);
for(int j=1;j<=m-k+1;j++){
int val=s[i][j+k-1]-s[i][j-1]+s[i+1][j+k-1]-s[i+1][j-1];
dp[j]=max(lmax[max(j-k,0)],rmax[min(m+1,j+k)])+val;
dp[j]=max(dp[j],newdpl[j]+val);
dp[j]=max(dp[j],newdpr[m-j-k+2]+val);
dpl[j]=dpr[m-j-k+2]=dp[j];
//cout<<i<<" "<<j<<" "<<dp[j]<<endl;
}
}
cout<<*max_element(dp.begin(),dp.end());
}