2021.11.11考试总结[冲刺NOIP模拟28]
T1 嗑瓜子
DP状态记现有几个瓜子,几个瓜子壳,按题意转移即可。
\(code:\)
T1
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL; typedef long double LD;
typedef unsigned long long ULL; typedef double DB;
#define int long long
#define freopen FL=freopen
FILE *FL;
const int Mxdt=100000;
static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
inline int read(){
int t=0,f=0;char v=gc();
while(v<'0')f|=(v=='-'),v=gc();
while(v>='0')t=(t<<3)+(t<<1)+v-48,v=gc();
return f?-t:t;
}
void write(int x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=2010,mod=998244353;
int n,inv[NN<<2],f[NN][NN<<1];
int qpow(int a,int b,int res=1){
for(;b;b>>=1,a=a*a%mod)
if(b&1) res=res*a%mod;
return res;
}
int dfs(int fi,int se){
if(f[fi][se]) return f[fi][se];
if(!fi) return 0;
int tmp=fi*inv[fi+se]%mod;
f[fi][se]=1;
(f[fi][se]+=dfs(fi-1,se+2)*tmp)%=mod;
if(se) (f[fi][se]+=dfs(fi,se-1)*(mod+1-tmp))%=mod;
return f[fi][se];
}
signed main(){
freopen("eat.in","r",stdin);
freopen("eat.out","w",stdout);
n=read();
for(int i=1;i<=(n*3);i++) inv[i]=qpow(i,mod-2);
write(dfs(n,0),'\n');
return 0;
}
T2 第k大查询
考虑每个数的贡献,枚举每个数左边大于它数的个数,按点权顺序加点保证当前序列中所有数都大于它,然后双向链表维护即可。
\(code:\)
T2
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL; typedef long double LD;
typedef unsigned long long ULL; typedef double DB;
#define freopen FL=freopen
FILE *FL;
const int Mxdt=100000;
static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
inline int read(){
int t=0,f=0;char v=gc();
while(v<'0')f|=(v=='-'),v=gc();
while(v>='0')t=(t<<3)+(t<<1)+v-48,v=gc();
return f?-t:t;
}
void write(LL x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=500001;
int n,k,pos[NN],tol[NN],tor[NN];
int pl,pr,lenl[50],lenr[50];
LL ans;
set<int>s;
void add(int p){
s.insert(p);
if(s.find(p)!=s.begin()){
pl=*(--s.find(p));
tor[pl]=p; tol[p]=pl;
}
if(s.find(p)!=--s.end()){
pr=*(++s.find(p));
tor[p]=pr; tol[pr]=p;
}
}
void solve(int p){
memset(lenl,0,sizeof(lenl));
memset(lenr,0,sizeof(lenr));
add(pos[p]); pl=pr=pos[p];
for(int i(0);i<=k;i++){
if(pl>=1){ lenl[i]=pl-tol[pl]; pl=tol[pl]; }
if(pr<=n){ lenr[i]=tor[pr]-pr; pr=tor[pr]; }
if(pl<1&&pr>n) break;
}
for(int i(0),j(k);i<=k;i++,j--) if(lenl[i]&&lenr[j])
ans+=1ll*p*lenl[i]*lenr[j];
}
signed main(){
freopen("kth.in","r",stdin);
freopen("kth.out","w",stdout);
n=read(); k=read()-1;
for(int i(1);i<=n;i++)
pos[read()]=i,tor[i]=n+1;
for(int i(n);i;i--) solve(i);
write(ans,'\n');
return 0;
}
T3 树上路径
两条路径总可以被一条边分到两部分。记 \(down_v\) 为 \(v\) 子树的直径, \(up_v\) 为过 \(v\) 父亲而不过 \(v\) 的最长链,就可以用这两个数组更新答案。更新时取后缀最大值。
考虑如何求。 \(down\) 直接树规,求 \(up_v\) 有两种情况,记 \(u\) 为 \(v\) 父亲:
-
组成 \(up_u\) 的两条链,一条是 \(u\) 上方的链,一条是 \(u\) 下方且不属于子树 \(v\) 的链
-
组成 \(up_v\) 的两条链都是 \(u\) 下方且不属于子树 \(v\) 的链
因此维护 \(v\) 子树内最长链,次长链和次次长链,与 \(v\) 从父亲方向来的最长链,分类讨论转移。
参考博客:Blog
\(code:\)
T3
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL; typedef long double LD;
typedef unsigned long long ULL; typedef double DB;
#define freopen FL=freopen
FILE *FL;
const int Mxdt=100000;
static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
inline int read(){
int t=0,f=0;char v=gc();
while(v<'0')f|=(v=='-'),v=gc();
while(v>='0')t=(t<<3)+(t<<1)+v-48,v=gc();
return f?-t:t;
}
void write(LL x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=500010;
int n,idx,head[NN];
int up[NN],down[NN],f[NN][4],g[NN][2];
int mx[NN];
LL ans;
struct edge{ int to,nex; }e[NN<<1];
void add(int a,int b){
e[++idx]=(edge){b,head[a]}; head[a]=idx;
e[++idx]=(edge){a,head[b]}; head[b]=idx;
}
void dfs1(int s,int fa){
for(int v,i=head[s];i;i=e[i].nex) if((v=e[i].to)!=fa){
dfs1(v,s);
int tmp=f[v][0]+1;
if(tmp>f[s][0]) swap(f[s][0],tmp);
if(tmp>f[s][1]) swap(f[s][1],tmp);
if(tmp>f[s][2]) swap(f[s][2],tmp);
ckmax(down[s],down[v]);
}
ckmax(down[s],f[s][0]+f[s][1]);
}
void dfs2(int s,int fa){
for(int v,i=head[s];i;i=e[i].nex) if((v=e[i].to)!=fa){
int tmp=down[v];
if(tmp>g[s][0]) swap(g[s][0],tmp);
if(tmp>g[s][1]) swap(g[s][1],tmp);
}
for(int v,i=head[s];i;i=e[i].nex) if((v=e[i].to)!=fa){
if(f[s][0]==f[v][0]+1){
f[v][3]=max(f[s][1],f[s][3])+1;
up[v]=max(f[s][2],f[s][3])+f[s][1];
} else if(f[s][1]==f[v][0]+1){
f[v][3]=max(f[s][0],f[s][3])+1;
up[v]=max(f[s][2],f[s][3])+f[s][0];
} else{
f[v][3]=max(f[s][0],f[s][3])+1;
up[v]=max(f[s][1],f[s][3])+f[s][0];
}
if(g[s][0]==down[v]) ckmax(up[v],g[s][1]);
else ckmax(up[v],g[s][0]);
dfs2(v,s);
}
}
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();
for(int a,b,i=1;i<n;i++)
a=read(),b=read(),add(a,b);
dfs1(1,0); dfs2(1,0);
for(int i=2;i<=n;i++){
ckmax(mx[down[i]+1],up[i]+1);
ckmax(mx[up[i]+1],down[i]+1);
}
for(int i=n;i;i--) ckmax(mx[i],mx[i+1]),ans+=mx[i];
write(ans,'\n');
return 0;
}
T4 糖

处理 \(3(2)\) 时可以视为每次遇到较优的位置就操作,并记购买价格为 \(sell\) ,这样迭代到最后可以保证操作位置是最优的。
\(code:\)
T4
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL; typedef long double LD;
typedef unsigned long long ULL; typedef double DB;
#define int long long
typedef pair<int,int> PII;
#define mpr make_pair
#define fi first
#define se second
#define freopen FL=freopen
FILE *FL;
const int Mxdt=100000;
static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
inline int read(){
int t=0,f=0;char v=gc();
while(v<'0')f|=(v=='-'),v=gc();
while(v>='0')t=(t<<3)+(t<<1)+v-48,v=gc();
return f?-t:t;
}
void write(int x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=200010;
int n,c,ans,a[NN],b[NN],s[NN];
int in;
deque<PII>q;
signed main(){
freopen("candy.in","r",stdin);
freopen("candy.out","w",stdout);
n=read(); c=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=0;i<n;i++) b[i]=read(),s[i]=read();
in=c-(a[1]-a[0]); ans=(a[1]-a[0])*b[0]; q.push_back(mpr(in,b[0]));
for(int i=1;i<n;i++){
while(!q.empty()&&q.back().se>b[i]) in-=q.back().fi,q.pop_back();
if(in<c) q.push_back(mpr(c-in,b[i])); in=c;
while(!q.empty()&&q.front().se<=s[i])
in-=q.front().fi,ans-=q.front().fi*(s[i]-q.front().se),q.pop_front();
if(in<c) q.push_front(mpr(c-in,s[i])); in=c;
while(a[i]<a[i+1]){
PII tmp=q.front(); q.pop_front();
if(a[i]+tmp.fi<=a[i+1]){
in-=tmp.fi;
ans+=tmp.fi*tmp.se;
a[i]+=tmp.fi;
} else{
in-=a[i+1]-a[i];
ans+=(a[i+1]-a[i])*tmp.se;
tmp.fi-=a[i+1]-a[i];
a[i]=a[i+1];
q.push_front(tmp);
}
}
}
write(ans,'\n');
return 0;
}

浙公网安备 33010602011771号