【考试总结】2022-04-06
Fable
考虑冒泡排序的性质即如果某个数前面有元素大于之,那么一定在这轮这种元素数量恰好减少 \(1\)
那么可以计算出来经过 \(k\) 轮后的每个位置的数字前面有几个元素大于之,然后使用数据结构维护最小的前方没有元素大于之的数即可
这样子的数据结构需要支持区间减法,所以写了线段树
Code Display
const int N=200010;
int n,m,a[N],k,b[N];
struct Fenwick{
int c[N];
inline int query(int x){
int res=0;
for(;x<=m;x+=x&(-x)) res+=c[x];
return res;
}
inline void insert(int x,int v){
for(;x;x-=x&(-x)) c[x]++;
return ;
}
}T;
int lsh[N];
bool vis[N];
vector inv[N];
struct Seg{
int Mn[N<<2],tag[N<<2];
inline void push_tag(int p,int v){Mn[p]+=v; tag[p]+=v;}
inline void push_down(int p){
if(tag[p]){
push_tag(p<<1,tag[p]);
push_tag(p<<1|1,tag[p]);
tag[p]=0;
} return ;
}
inline void push_up(int p){
Mn[p]=min(Mn[p<<1],Mn[p<<1|1]);
return ;
}
inline void build(int p,int l,int r){
if(l==r){
Mn[p]=inv[l].back();
return ;
}
int mid=(l+r)>>1;
build(p<<1,l,mid); build(p<<1|1,mid+1,r);
return push_up(p);
}
inline void upd(int p,int l,int r,int st,int ed){
if(st<=l&&r<=ed) return push_tag(p,-1);
int mid=(l+r)>>1; push_down(p);
upd(p<<1,l,mid,st,ed);
if(ed>mid) upd(p<<1|1,mid+1,r,st,ed);
return push_up(p);
}
inline int dfs(int p,int l,int r){
if(l==r){
inv[l].pop_back();
Mn[p]=inv[l].size()?inv[l].back()+tag[p]:n+1;
return l;
}
int mid=(l+r)>>1,res=0;
push_down(p);
if(Mn[p<<1]==0) res=dfs(p<<1,l,mid);
else res=dfs(p<<1|1,mid+1,r);
push_up(p);
return res;
}
}seg;
signed main(){
freopen("fable.in","r",stdin); freopen("fable.out","w",stdout);
n=read(); k=read();
rep(i,1,n) a[i]=read(),lsh[i]=a[i];
sort(lsh+1,lsh+n+1);
m=unique(lsh+1,lsh+n+1)-lsh-1;
rep(i,1,n){
a[i]=lower_bound(lsh+1,lsh+m+1,a[i])-lsh;
b[i]=T.query(a[i]+1);
T.insert(a[i],1);
}
rep(i,1,n){
b[i]=max(0ll,b[i]-k);
inv[a[i]].emplace_back(b[i]);
}
rep(i,1,m){
sort(inv[i].begin(),inv[i].end());
reverse(inv[i].begin(),inv[i].end());
}
seg.build(1,1,m);
rep(i,1,n){
int v=seg.dfs(1,1,m);
print(lsh[v]);
if(v>1) seg.upd(1,1,m,1,v-1);
}
return 0;
}
Fiend
逆序对奇数减,偶数加,符合行列式定义,平凡做法就是直接求行列式 \(A_{i,l_i\sim r_i}=1\) 的值
但是发现每行的 \(1\) 是连续的,可以对于每列找到所有这列有元的行中右端点最小的,并将其它的行 \(l_j\leftarrow r_i+1\)
如果需要交换行那么需要将行列式的值取反
那么使用左偏树维护所有的左端点,要支持的操作是取出堆顶,合并两个堆
同时在左偏树的每个节点维护点对应的行编号以及逆映射,交换两行时就能维护正确的行号了
Code Display
const int N=1e5+10;
int n,nds;
int rt[N],ls[N],rs[N],dep[N],mp[N],id[N],val[N];
inline int merge(int x,int y){
if(!x||!y) return x+y;
if(val[x]>val[y]) swap(x,y);
rs[x]=merge(rs[x],y);
if(dep[rs[x]]>dep[ls[x]]) swap(ls[x],rs[x]);
dep[x]=dep[rs[x]]+1;
return x;
}
inline void pop(int &x){x=merge(ls[x],rs[x]);}
inline int New(int v,int lin){
int p=++nds;
mp[lin]=p; id[p]=lin;
dep[p]=1;
val[p]=v;
return p;
}
signed main(){
freopen("fiend.in","r",stdin); freopen("fiend.out","w",stdout);
int T=read(); while(T--){
n=read();
for(int i=1;i<=n;++i){
int l=read(),r=read();
rt[l]=merge(rt[l],New(r,i));
}
int ans=1;
for(int i=1;i<=n;++i){
if(!rt[i]){ans=0; break;}
if(id[rt[i]]!=i){
id[mp[i]]=id[rt[i]];
mp[id[rt[i]]]=mp[i];
ans*=-1;
}
int v=val[rt[i]];
pop(rt[i]);
if(val[rt[i]]==v){ans=0; break;}
if(v0) puts("Y");
else if(ans==0) puts("D");
else puts("F");
for(int i=1;i<=n;++i){
dep[i]=ls[i]=rs[i]=val[i]=0;
id[i]=mp[i]=0;
rt[i]=0;
}
nds=0;
}
return 0;
}
Flair
使用互质数对 \((a,b)\) 不能表示出来的最大数是 \(ab-a-b\) 推导不互质的数对可以发现:在 \(x>10000\) 后,每个菜品数量所消耗的钱数是每 \(\rm GCD\{c\}\) 一循环的,同时在每个循环节中都是以 \(\rm GCD-1\to 0\) 方式递减
那么使用倍增 \(\rm MTT\) 配合 \(f_{i,j}=pf_{i-1,j-1}+(1-p)f_{i,j}\) 计算 \(G_r=\sum\limits_{i\equiv r\bmod\ {\rm{GCD}}} f_{n,i}\) 后可以得到选择菜品数较多时候的答案
对于选择菜品数对应的浪费的钱数未进入循环节的情况,可以另做一个背包来算浪费数,而对应的选菜概率的计算可以使用 组合数等于下降幂除阶乘 的方式来解决
Code Display
const int N=10010;
int n,m,p,c[N];
const double pi=acos(-1);
struct node{
long double x,y;
node(){}
node(long double xx,long double yy){x=xx; y=yy; return ;}
node operator +(const node &a)const{return node(x+a.x,y+a.y);}
node operator -(const node &a)const{return node(x-a.x,y-a.y);}
node operator *(const node &a)const{return node(x*a.x-y*a.y,x*a.y+y*a.x);}
inline node conj(){return node(x,-y);}
inline void clear(){x=y=0;}
}A0[N<<2],B0[N<<2],A1[N<<2],B1[N<<2],W[N<<2],F[N<<2],G[N<<2],H[N<<2];
int r[N<<2];
inline void FFT(node *f,int lim,int opt){
rep(i,0,lim-1){
if(i>1; node tt;
W[0]=node(1,0);
rep(i,1,len-1) W[i]=node(cos(pi/len*i),opt*sin(pi/len*i));
for(int k=0;k>1]>>1|((i&1)?(lim>>1):0);
}
for(int i=0;i>15,A0[i].y=a[i]&32767; FFT(A0,lim,1);
for(int i=0;i>15,B0[i].y=b[i]&32767; FFT(B0,lim,1);
for(int i=0;i dp(20010,1000100);
dp[0]=0;
int up=2e4;
for(int i=1;i<=m;++i){
for(int j=0;j+c[i]<=up;++j) if(dp[j]==0) dp[j+c[i]]=0;
}
for(int i=up-1;i>=0;--i) ckmin(dp[i],dp[i+1]+1);
rep(i,0,g-1) poly[i]=bas[i]=0;
poly[0]=1;
bas[1]=p; bas[0]=100-p;
int y=n,lenb=2,lenp=1;
while(y){
if(y&1){
MTT(poly,bas,poly,lenp,lenb);
lenp=min(g,lenp+lenb-1);
}
MTT(bas,bas,bas,lenb,lenb);
lenb=min(g,lenb*2-1);
y>>=1;
}
int ans=0;
int dfac=1,pwp=1,curpw=ksm(100-p,n),Inv=ksm(100-p,mod-2);
for(int i=0;i<=min(n,10000ll);++i){
int contr=mul(mul(pwp,curpw),mul(dfac,ifac[i]));
ckadd(ans,mul(dp[i],contr));
ckdel(poly[i%g],contr);
ckmul(dfac,n-i);
ckmul(pwp,p);
ckmul(curpw,Inv);
}
for(int i=1;i