Atcoder Beginner Contest 369
Atcoder Beginner Contest 369
唯一的一发罚时吃在 A 题,消息了。
A
挂一发的原因是以为 \(A,B\) 都是一位数,然后循环范围开了 \([-10,20]\)。
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
int x,y,a[4],ans=0;
cin>>x>>y;
for(int i=-100;i<=200;i++)
{
a[1]=x,a[2]=y,a[3]=i;
sort(a+1,a+4);
ans+=a[3]-a[2]==a[2]-a[1];
}
cout<<ans;
return 0;
}
B
Simulate
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
int n,l=-1,r=-1;
cin>>n;
int ans=0;
while(n--)
{
int p;char c;
cin>>p>>c;
if(c=='L')
{
if(~l)ans+=abs(l-p);
l=p;
}
else
{
if(~r)ans+=abs(r-p);
r=p;
}
}
cout<<ans;
return 0;
}
C
直接差分然后变成全相等的段数。这是简单的。\(\Theta(n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,a[N];
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>n;
ll ans=0;
for(int i=1,lst=1;i<=n;i++)
{
cin>>a[i];
if(lst<i-1&&a[i]-a[i-1]!=a[i-1]-a[i-2])lst=i-1;
ans+=i-lst+1;
}
cout<<ans;
return 0;
}
D
直接令 \(dp_{i,0/1}\) 表示前 \(i\) 个怪兽,打了偶数/奇数个的最大得分,维护个前缀 \(\max\) 即可。\(\Theta(n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
ll dp[N][2],mx[N][2];
int n;
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>n;
mx[0][1]=LLONG_MIN;
for(int i=1,x;i<=n;i++)
{
cin>>x;
dp[i][0]=mx[i-1][1]+(x<<1);
dp[i][1]=mx[i-1][0]+x;
mx[i][0]=max(mx[i-1][0],dp[i][0]);
mx[i][1]=max(mx[i-1][1],dp[i][1]);
}
cout<<max(mx[n][0],mx[n][1]);
return 0;
}
E
你先钦定那 \(k\) 条边经过的顺序和方向,那么中间的过程就是从一个终点到另一个起点的最短路,直接预先 Floyd 出来即可。\(\Theta(n^3+m+qk!2^kk)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=405,M=200005;
int n,m,q,u[M],v[M],w[M];
ll dis[N][N];
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>n>>m;
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=m;i++)
{
cin>>u[i]>>v[i]>>w[i];
dis[u[i]][v[i]]=dis[v[i]][u[i]]=min(dis[u[i]][v[i]],(ll)w[i]);
}
for(int i=1;i<=n;i++)dis[i][i]=0;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
cin>>q;
while(q--)
{
int k,id[6];
cin>>k;
for(int i=1;i<=k;i++)cin>>id[i];
sort(id+1,id+k+1);
ll ans=LLONG_MAX;
do
{
for(int j=0;j<1<<k;j++)
{
ll cur=0;
for(int i=1;i<=k;i++)cur+=w[id[i]];
for(int i=1;i<=k+1;i++)cur+=dis[i==1?1:(j>>i-2)&1?v[id[i-1]]:u[id[i-1]]][i>k?n:(j>>i-1)&1?u[id[i]]:v[id[i]]];
ans=min(ans,cur);
}
}while(next_permutation(id+1,id+k+1));
cout<<ans<<endl;
}
return 0;
}
F
考虑 dp,令 \(dp_i\) 表示走到第 \(i\) 枚金币的最大拿到的金币数量。那么转移就是
\[dp_i=\max_{\substack{x_i\ge x_j\\y_i\ge y_j}}\{dp_j\}+1
\]
直接按 \(x\) 排序,那么就是 \(y\) 坐标上的单点取 \(\max\) /前缀求 \(\max\),直接树状数组。构造不难。\(\Theta(n\log W)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int H,W,n;
PII p[N],dp[N];
PII f[N];
void put(int p,PII v){while(p<=W)f[p]=max(f[p],v),p+=p&-p;}
PII get(int p){PII res={-1,0};while(p)res=max(res,f[p]),p^=p&-p;return res;}
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>H>>W>>n;
for(int i=1;i<=n;i++)cin>>p[i].fi>>p[i].se;
p[0]={1,1},p[n+1]={H,W};
sort(p+1,p+n+1);
fill(f+1,f+n+1,MP(INT_MIN,0));
put(1,dp[0]);
for(int i=1;i<=n+1;i++)
{
dp[i]=get(p[i].se);
put(p[i].se,{++dp[i].fi,i});
}
string ans;
cout<<dp[n+1].fi-1<<endl;
for(int i=n+1;i;i=dp[i].se)
{
for(int j=1;j<=p[i].fi-p[dp[i].se].fi;j++)ans+='D';
for(int j=1;j<=p[i].se-p[dp[i].se].se;j++)ans+='R';
}
reverse(ALL(ans));
cout<<ans;
return 0;
}
G
不难发现当钦定点集时答案是虚树边权和的两倍。
直接费用流思想可以得到一个贪心:每次取一个离根最远的点,把答案加上其到根的距离,然后把它到根的路径上的边权全部置 \(0\)。
直接 dfn 序拍平维护点到根距离,那么置 \(0\) 就相当于区间减,查询是全局 \(\max\),直接线段树,\(\Theta(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n;
vector<PII>g[N];
struct node
{
ll add,mx;
int xid;
}tr[1<<19];
#define lid (id<<1)
#define rid (lid|1)
#define mid (l+r>>1)
#define rmd (mid+1)
void build(int l=1,int r=n,int id=1)
{
tr[id].add=tr[id].mx=0,tr[id].xid=r;
if(l==r)return;
build(l,mid,lid),build(rmd,r,rid);
}
void pushdown(int id)
{
ll &ad=tr[id].add;
tr[lid].add+=ad,tr[rid].add+=ad,tr[lid].mx+=ad,tr[rid].mx+=ad;
ad=0;
}
void add(int L,int R,int v,int l=1,int r=n,int id=1)
{
if(L==l&&R==r)return tr[id].mx+=v,void(tr[id].add+=v);
pushdown(id);
if(L<=mid)add(L,min(R,mid),v,l,mid,lid);
if(R>=rmd)add(max(L,rmd),R,v,rmd,r,rid);
tie(tr[id].mx,tr[id].xid)=max(MP(tr[lid].mx,tr[lid].xid),MP(tr[rid].mx,tr[rid].xid));
}
int dfn[N],rdn[N],ed[N],df,f[N],fw[N];
void dfs(int x,int fa,int wg)
{
dfn[x]=++df,rdn[df]=x,f[x]=fa,fw[x]=wg;
for(auto y:g[x])
if(y.fi!=fa)dfs(y.fi,x,y.se);
ed[x]=df;
add(dfn[x],ed[x],wg);
}
bool vis[N];
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
// int _;cin>>_;while(_--)
cin>>n;
for(int i=1,u,v,w;i<n;i++)
cin>>u>>v>>w,g[u].PB(v,w),g[v].PB(u,w);
build();
dfs(1,0,0);
ll ans=0;
vis[1]=1;
for(int i=1;i<=n;i++)
{
cout<<((ans+=tr[1].mx)<<1)<<endl;
for(int j=rdn[tr[1].xid];!vis[j];j=f[j])
vis[j]=1,add(dfn[j],ed[j],-fw[j]);
}
return 0;
}