【考试总结】2022-01-25

体育测试

ai>0i 为第一类数字,否则为第二类数字

fi,j 表示前 i 个位置中填入了 j 第二类数字的方案数

根据状态定义不难得到可以放置的第二类数字的数量,那此处放入第二类数字的贡献系数就是可以放入的数量

对于放置第一类数字的情况,我们只在每个数必须放置时将方案数附加到转移式子中

仍然根据状态定义 计算出来 “可以”(不是已经)放入当前位置之前的空位处的的第一类数字数量,同时也可以算出来 “必须” 放入当前位置之前的第一类数字数量

使用一个填入空位的排列数即可得到转移式子,注意根据状态定义,当前位置即使放的是第二类数字也要附加排列数

Code Display
const int N=5010;
int dp[N][N],a[N],n,ton[N],pre[N];
int fac[N],ifac[N];
inline int A(int n,int m){return n>=m?mul(fac[n],ifac[n-m]):0;}
signed main(){
	fac[0]=1; n=5000;
	for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],mod-2);
	Down(i,n,1) ifac[i-1]=mul(ifac[i],i);
	freopen("test.in","r",stdin); freopen("test.out","w",stdout);
	n=read(); rep(i,1,n){
		if((a[i]=read())>0) ton[a[i]]++;
		else pre[-a[i]]++;
	}
	rep(i,1,n) pre[i]+=pre[i-1];	
	dp[0][0]=1;
	int alr=0;
	for(int i=1;i<=n;++i){
		for(int j=0;j<=pre[i];++j){
			// prefix with length i and j second type numbers pushed
			int k=i-j-alr;
			ckadd(dp[i][j],mul(dp[i-1][j],A(k,ton[i])));
			if(j) ckadd(dp[i][j],mul(dp[i-1][j-1],mul(A(k,ton[i]),pre[i]-(j-1))));
		} 
		alr+=ton[i];
	}
	print(dp[n][pre[n]]);
	return 0;
}

贸易

先对于每个 i[1,n] 求出树上两点间路径长度每个 i 的方案数

不难发现对答案的贡献方式是关于 1pi 多项式,所以使用 【模板】多项式多点求值 即可完成本题

Code Display
const int N=3e5+10;
vector<int> g[N];
int r[N],W[N],n,Q;
inline void NTT(vector<int> &f,int lim,int opt){
    f.resize(lim);
    for(int i=0;i<lim;++i){
        r[i]=r[i>>1]>>1|((i&1)?(lim>>1):0);
        if(i<r[i]) swap(f[i],f[r[i]]);
    }
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1; W[0]=1; W[1]=ksm(3,(mod-1)/p);
        if(opt==-1) W[1]=ksm(W[1],mod-2);
        for(int j=2;j<len;++j) W[j]=mul(W[j-1],W[1]);
        for(int k=0;k<lim;k+=p){
            for(int l=k;l<k+len;++l){
                int tt=mul(f[l+len],W[l-k]);
                f[l+len]=del(f[l],tt);
                ckadd(f[l],tt);
            }
        }
    }
    if(opt==-1) for(int i=0,tmp=ksm(lim,mod-2);i<lim;++i) ckmul(f[i],tmp);
    return ;
}
inline poly Mul(poly a,poly b){
    int n=a.size(),m=b.size(),lim=1;
    while(lim<=(n+m)) lim<<=1;
    NTT(a,lim,1); NTT(b,lim,1);
    for(int i=0;i<lim;++i) ckmul(a[i],b[i]);
    NTT(a,lim,-1);
    a.resize(n+m-1);
    return a;
}
inline poly Inv(poly a,int len){
    if(len==1) return {ksm(a[0],mod-2)};
    vector<int> G0=Inv(a,(len+1)>>1);
    int lim=1; while(lim<=(len<<1)) lim<<=1;
    vector<int> tmp,G;
    rep(i,0,len-1) tmp.pb(a[i]);
    NTT(tmp,lim,1); NTT(G0,lim,1);
    for(int i=0;i<lim;++i) G.pb(mul(G0[i],del(2,mul(G0[i],tmp[i]))));
    NTT(G,lim,-1); G.resize(len);
    return G;
}
inline poly Mod(vector<int> F,vector<int> G){
    int n=F.size(),m=G.size(); if(n<m) return F;
    poly Fr=F,Gr=G,R;
    reverse(Fr.begin(),Fr.end());
    reverse(Gr.begin(),Gr.end()); Gr.resize(n-m+1);
    Gr=Inv(Gr,n-m+1);
    poly Q=Mul(Gr,Fr); Q.resize(n-m+1);
    reverse(Q.begin(),Q.end());
    Q=Mul(Q,G);
    rep(i,0,m-2) R.pb(del(F[i],Q[i]));
    return R;
}
bool vis[N];
int sizmx[N],siz[N],rt,nsum,ar[N],dmx;
inline void findrt(int x,int fat){
    sizmx[x]=0; siz[x]=1;
    for(auto t:g[x]) if(!vis[t]&&t!=fat){
        findrt(t,x); siz[x]+=siz[t];
        ckmax(sizmx[x],siz[t]);
    } 
    ckmax(sizmx[x],nsum-siz[x]);
    if(sizmx[x]<sizmx[rt]) rt=x;
    return ;
}
inline void get_node(int x,int fat,int ndep){
    ar[ndep]++; ckmax(dmx,ndep);
    for(auto t:g[x]) if(!vis[t]&&t!=fat){
        get_node(t,x,ndep+1);
    }
    return ;
}
poly f,qu,ans;
inline void upd(vector<int> p,int fl){
    f.resize(max(p.size(),f.size()));
    for(int i=0;i<p.size();++i) f[i]+=p[i]*fl;
    return ;
}
inline vector<int> turn(){
    vector<int> res; res.resize(dmx+1);
    for(int i=0;i<=dmx;++i) res[i]=ar[i],ar[i]=0;
    return res;
}
inline void solve(int x){
    vis[x]=1;
    int amx=0;
    vector<int> now={1};
    for(auto t:g[x]) if(!vis[t]){
        get_node(t,x,1);
        vector<int> p=turn();
        upd(Mul(p,p),-1);
        now.resize(max(now.size(),p.size()));
        for(int i=0;i<p.size();++i) now[i]+=p[i];
        dmx=0;
    }
    upd(Mul(now,now),1); now.clear(); now.shrink_to_fit();
    for(auto t:g[x]) if(!vis[t]){
        nsum=siz[t]; rt=0; findrt(t,0);
        solve(rt);
    }
    return ;
}
namespace Multi_Evaluation{
	vector<int> P[N];
    inline void prework(int p,int l,int r){
        if(l==r){P[p]={mod-qu[l],1}; return ;}
        int mid=(l+r)>>1; prework(p<<1,l,mid); prework(p<<1|1,mid+1,r);
        P[p]=Mul(P[p<<1],P[p<<1|1]);
		return ;
    }
    inline void solve(int p,int l,int r,vector<int> f){
        if(l==r) return ans.push_back(f[0]),void(); int mid=(l+r)>>1;
        solve(p<<1,l,mid,Mod(f,P[p<<1])); 
		solve(p<<1|1,mid+1,r,Mod(f,P[p<<1|1]));
        return ;
    }
    void solve(){
        int n=f.size()-1,m=qu.size();
        prework(1,0,m-1);
		solve(1,0,m-1,n>=m?Mod(f,P[1]):f);
        return ;
    }
}
signed main(){
	freopen("trade.in","r",stdin); freopen("trade.out","w",stdout);
    n=read(); Q=read(); 
    int invn=ksm(n,mod-2); ckmul(invn,invn);
    sizmx[0]=2e9;
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        g[u].pb(v),g[v].pb(u);
    }
    nsum=n; findrt(1,0); solve(rt);
	f[0]=n;
	int sf=0;
	for(auto t:f) ckadd(sf,t);
	static int buy[N],sell[N];
	rep(i,1,Q){
		buy[i]=read(),sell[i]=read();
		int a=read(),b=read();
		qu.push_back(del(1,mul(a,ksm(b,mod-2))));
	}
	Multi_Evaluation::solve();
	rep(i,1,Q) print(mul(invn,del(mul(sell[i],ans[i-1]),mul(sf,buy[i]))));
    return 0;
}

密码

使用相同的随机种子(可以是程序中写相同的数字)生成两组个数为 m,每个长度为 n0/1 串作为基底

这时在 encode 中传出的 ans 字符串表示这个串是不是需要被异或来得到题目中最原始的长度为 n 的串

可以对自由的 mk 个位置的字符串建立线性基来表示元初串和已经被钦定成 1 的位置的字符串的异或,这里线性基要记录每个基底在插入过程中被谁更新过

发现如果这 n+50 个自由串仍然不能让矩阵满秩的概率是非常小的

这其实类似于让 50+1 个串在某个位置都为 0/1 的概率,大约是 1250,已经到了可以忽略不计的地步了

实现的时候注意使用的是 mt19937 而不是 srand 即可

一些可能的暴力做法是将表示原串的部分中插入原串其中的钦定点的位置在原串前面用二进制表示出来,通过不同的表示原串的左界的设定可以得到 60 分,另外 15 分可以通过让两位之首为 1 来表示其是原串中的元素

听说这是通信题?感觉还是很有趣的(做这题时间长得以至于没发现多点求值板子,给出题人点(fa)赞(dao))

Code Display
const int N=1010,M=2050;
bitset<N> st[M],bas[N];
bitset<M> id[M];
int n;
inline void insert(int nid,bitset<N> now){
    bitset<M> ids; ids.set(nid);
    for(int i=n-1;~i;--i)if(now[i]){
        if(!bas[i][i]){
            bas[i]=now;
            id[i]=ids;
            break;
        }
        now^=bas[i];
        ids^=id[i];
    } return ;
}
void encoder(int NN, int m, int k, const char* a, const char* b, char* ans){
	rep(i,0,m-1) ans[i]=b[i];
	mt19937 ee(20041014);
	n=NN;
    rep(i,0,m-1) rep(j,0,n-1) st[i][j]=ee()&1;
	bitset<N> tmp; tmp.reset();
	rep(i,0,n-1) tmp[i]=a[i]-'0';
	bitset<M> mark; mark.reset();
	rep(i,0,m-1) if(b[i]=='1') tmp^=st[i],mark[i]=1; else if(b[i]=='?') insert(i,st[i]);
	Down(i,n-1,0) if(tmp[i]){
	    mark^=id[i];
	    tmp^=bas[i];
	}
	assert(!tmp.any());
	rep(i,0,m-1) ans[i]=mark[i]+'0';
	return ;
}
void decoder(int n, int m, const char* a, char* ans){
    mt19937 ee(20041014);
    rep(i,0,m-1) rep(j,0,n-1) st[i][j]=ee()&1;
    bitset<N> res; res.reset();
    rep(i,0,m-1) if(a[i]=='1') res^=st[i];
    rep(i,0,n-1) ans[i]=res[i]+'0';
	return ;
}
posted @   没学完四大礼包不改名  阅读(83)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示