2021.11.10考試總結[衝刺NOIP模擬27]
阿新 • • 發佈:2021-11-10
T1開掛 T2叄仟柒佰萬 T3超級加倍 T4歡樂豆
T1 開掛
因為可以隨便選 \(b\) ,所以可以先對 \(a\) \(b\) 排序。不難想到最優策略是儘量把操作集中到一個點上,再把較小的 \(b\) 分配給它。
因此應讓開始偏小的 \(a\) 最終儘量大。這個倒序掃描就可以實現。中間會出現若干空隙,考慮最優策略,肯定是先將數填到最近的空隙裡,用棧維護可以完成。
\(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;
typedef pair<int,int> PII;
#define fi first
#define se second
#define mpr make_pair
#define int ULL
#define pb push_back
const int Mxdt=100000;
inline char gc(){
static char buf[Mxdt],*p1=buf,*p2=buf;
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){
char ch[50]; 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=1000010;
int n,ans,a[NN],b[NN];
int it,top,pre;
PII stk[NN];
vector<int>op;
signed main(){
freopen("openhook.in","r",stdin);
freopen("openhook.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) b[i]=read();
sort(a+1,a+n+1); sort(b+1,b+n+1);
pre=a[n];
for(it=n-1;a[it]==a[n];it--)
++pre,op.pb(pre-a[it]);
while(it){
int lst=it,cnt=-1;
if(a[it]<a[it+1]-1) stk[++top]=mpr(a[it]+1,a[it+1]-1);
while(a[it]==a[lst]) --it,++cnt;
while(cnt&&top){
while(cnt&&stk[top].fi<=stk[top].se)
--cnt,op.pb(stk[top].fi-a[lst]),++stk[top].fi;
if(stk[top].fi>stk[top].se) --top;
}
while(cnt) --cnt,++pre,op.pb(pre-a[lst]);
}
sort(op.begin(),op.end(),[](int x,int y)->bool{return x>y;});
for(int i=0;i<op.size();i++) ans+=op[i]*b[i+1];
write(ans,'\n');
return 0;
}
T2 叄仟柒佰萬
發現序列中合法的 \(mex\) 值只能有一個,可以用桶找出這個 \(mex\) 。
考慮暴力 \(n^2\) DP,可以預處理出所有 \(mex\) 合法的區間,設 \(f_i\) 為當前最後一個區間右端點為 \(i\) 的方案數,轉移時列舉右端點對應的左端點,將方案數加和。
在右端點 \(r\) 增加時,最靠右的合法左端點是單調不降的,因此預處理可以通過單調指標加桶 \(O(n)\) 完成。轉移時,每個右端點的合法左端點一定是從 \(1\) 開始的一段連續點。字首和優化可以達到 \(O(n)\) 。
\(code:\)
T2
# pragma GCC optimize(12)
#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;
const int Mxdt=100000;
inline char gc(){
static char buf[Mxdt],*p1=buf,*p2=buf;
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){
char ch[50]; 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=37000010,mod=1e9+7;
int t,n,mex,a[NN],f[NN];
int buc[NN];
int qmod(int a){ return a<0?(a+mod):(a>=mod?a-mod:a); }
int qpow(int a,int b,int res=1){
for(;b;b>>=1,a=1ll*a*a%mod)
if(b&1) res=1ll*res*a%mod;
return res;
}
void read_a(){
f[1]=-1;
if(n<37000000)
for(int i=1;i<=n;i++) buc[(a[i]=read())]=1,f[i]=-1;
else{
int x=read(),y=read();
for(int i=2;i<=n;i++)
buc[(a[i]=(1ll*a[i-1]*x+y+i)&262143)]=1,f[i]=-1;
}
while(buc[mex]) ++mex;
}
void clear(){
mex=0;
for(int i=1;i<=n;i++)
buc[i]=f[i]=buc[a[i]]=0;
}
void init(){
if(n<37000000) for(int i=1;i<=n;i++) buc[a[i]]=0;
else memset(buc,0,sizeof(buc));
int it=0,now=0,pos=0;
while(now!=mex){
++buc[a[++pos]];
while(buc[now]) ++now;
} --buc[a[pos]];
for(int i=pos;i<=n;i++){
++buc[a[i]];
while(buc[a[it+1]]>1||a[it+1]>mex)
++it,--buc[a[it]];
f[i]=it;
}
f[0]=buc[0]=1;
}
signed main(){
freopen("clods.in","r",stdin);
freopen("clods.out","w",stdout);
t=read();
while(t--){
n=read(); read_a();
if(!mex){ write(qpow(2,n-1),'\n'); goto nxt; }
init();
for(int i=1;i<=n;i++){
if(f[i]>=0) f[i]=buc[f[i]];
else f[i]=0;
buc[i]=qmod(buc[i-1]+f[i]);
}
write(f[n],'\n');
nxt: clear();
}
return 0;
}
T3 超級加倍
一個牛B的東西: \(kruscal\) 重構樹。按點權大小建樹,可以滿足原樹上路徑 \((x,y)\) 上最大或最小值在重構樹上為 \(x,y\) 的 \(lca\) 。
因此建出樹 \(t1,t2\) ,分別為按點權遞增,遞減建出的重構樹。那麼要求的答案實際上就是滿足 \(x\) 在 \(t1\) 中為 \(y\) 祖先,在 \(t2\) 中為 \(y\) 後代的點對 \((x,y)\) 個數。本質上是個偏序問題,求出 \(t1\) 中的 \(dfs\) 序,在 \(t2\) 中用樹狀陣列維護祖先鏈並查詢即可。
\(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 pb push_back
const int Mxdt=100000;
inline char gc(){
static char buf[Mxdt],*p1=buf,*p2=buf;
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){
char ch[50]; 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=2000010;
int n,idx,prt[NN],heade[NN],headx[NN],headn[NN];
int cnt,up[NN],dn[NN];
LL ans;
struct Edge{
int to,nex;
Edge(){}
Edge(int t,int x){ to=t; nex=x; }
}tx[NN<<1],tn[NN<<1],te[NN<<1];
void addx(int a,int b){
tx[++idx]=Edge(b,headx[a]); headx[a]=idx;
tx[++idx]=Edge(a,headx[b]); headx[b]=idx;
}
void addn(int a,int b){
tn[++idx]=Edge(b,headn[a]); headn[a]=idx;
tn[++idx]=Edge(a,headn[b]); headn[b]=idx;
}
void adde(int a,int b){
te[++idx]=Edge(b,heade[a]); heade[a]=idx;
te[++idx]=Edge(a,heade[b]); heade[b]=idx;
}
namespace DSU{
int FA[NN];
int getf(int x){ return FA[x]==x?x:FA[x]=getf(FA[x]); }
void merge(int x,int y){
x=getf(x); y=getf(y);
if(x==y) return;
FA[y]=x;
}
void build(){
idx=0;
for(int i=1;i<=n;i++) FA[i]=i;
for(int i=1;i<=n;i++)
for(int j=heade[i],v=te[j].to;j;j=te[j].nex,v=te[j].to)
if(v<i){ addx(i,getf(v)); merge(i,v); }
idx=0;
for(int i=1;i<=n;i++) FA[i]=i;
for(int i=n;i>=1;i--)
for(int j=heade[i],v=te[j].to;j;j=te[j].nex,v=te[j].to)
if(v>i){ addn(i,getf(v)); merge(i,v); }
}
} using namespace DSU;
namespace BIT{
LL c[NN];
void insert(int pos,int x){ while(pos<=n){ c[pos]+=x; pos+=pos&-pos; } }
LL query(int pos,LL x=0){ while(pos){ x+=c[pos]; pos-=pos&-pos; } return x; }
LL calc(int l,int r){ return query(r)-query(l-1); }
} using namespace BIT;
void dfsx(int s,int f){
ans+=calc(up[s],dn[s]);
insert(up[s],1);
for(int v,i=headx[s];i;i=tx[i].nex)
if((v=tx[i].to)!=f) dfsx(v,s);
insert(up[s],-1);
}
void dfsn(int s,int f){
up[s]=++cnt;
for(int v,i=headn[s];i;i=tn[i].nex)
if((v=tn[i].to)!=f) dfsn(v,s);
dn[s]=cnt;
}
signed main(){
freopen("charity.in","r",stdin);
freopen("charity.out","w",stdout);
n=read(); idx=read();
for(int a,i=2;i<=n;i++)
a=read(),adde(i,a);
build(); dfsn(1,0); dfsx(n,0);
write(ans,'\n');
return 0;
}