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<int> 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(v<n) rt[v+1]=merge(rt[v+1],rt[i]);
}
if(ans>0) 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<r[i]) swap(f[i],f[r[i]]);
}
for(int p=2;p<=lim;p<<=1){
int len=p>>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<lim;k+=p){
for(int l=k;l<k+len;++l){
tt=f[l+len]*W[l-k];
f[len+l]=f[l]-tt,f[l]=f[l]+tt;
}
}
}
if(opt==-1) rep(i,0,lim-1) f[i].x/=lim;
}
int poly[N],bas[N],g;
inline void MTT(int *a,int *b,int *res,int n,int m){
int lim=1; while(lim<=(n+m)) lim<<=1;
rep(i,0,lim-1){
A0[i].x=A0[i].y=0;
B1[i].x=B1[i].y=0;
B0[i].x=B0[i].y=0;
A1[i].x=A1[i].y=0;
H[i].x=H[i].y=0;
G[i].x=G[i].y=0;
F[i].x=F[i].y=0;
r[i]=r[i>>1]>>1|((i&1)?(lim>>1):0);
}
for(int i=0;i<n;++i) A0[i].x=a[i]>>15,A0[i].y=a[i]&32767; FFT(A0,lim,1);
for(int i=0;i<m;++i) B0[i].x=b[i]>>15,B0[i].y=b[i]&32767; FFT(B0,lim,1);
for(int i=0;i<lim;++i) A1[i]=A0[i?lim-i:0].conj(),B1[i]=B0[i?lim-i:0].conj();
node p,q;
for(int i=0;i<lim;++i){
p=A0[i],q=A1[i];
A0[i]=(p+q)*node(0.5,0),A1[i]=(q-p)*node(0,0.5);
p=B0[i],q=B1[i];
B0[i]=(p+q)*node(0.5,0),B1[i]=(q-p)*node(0,0.5);
F[i]=A0[i]*B0[i]*node(1,0);
G[i]=A0[i]*B1[i]*node(1,0)+A1[i]*B0[i]*node(1,0);
H[i]=A1[i]*B1[i]*node(1,0);
}
FFT(F,lim,-1); FFT(G,lim,-1); FFT(H,lim,-1);
int up=min(g,n+m-1);
rep(i,0,up-1) res[i]=0;
for(int i=0;i<lim;++i) res[i%g]+=(int)(F[i].x+0.5)%mod*((1<<30)%mod)%mod+(int)(G[i].x+0.5)%mod*(1<<15)%mod+(int)(H[i].x+0.5)%mod;
for(int i=0;i<up;++i) res[i]=(res[i]%mod+mod)%mod;
}
int ifac[N];
signed main(){
freopen("flair.in","r",stdin); freopen("flair.out","w",stdout);
n=1e4; ifac[0]=1;
for(int i=1;i<=n;++i) ifac[i]=mul(ifac[i-1],ksm(i,mod-2));
int T=read(); while(T--){
n=read(); m=read(); p=read();
g=-1;
rep(i,1,m) g=g==-1?c[i]=read():__gcd(g,c[i]=read());
sort(c+1,c+m+1);
vector<int> 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<g;++i) ckadd(ans,mul(poly[i],g-i));
print(ans);
}
return 0;
}