• PKUWC&SC 2018 刷题记录


    PKUWC&SC 2018 刷题记录

    minimax

    线段树合并的题,似乎并不依赖于二叉树。

    之前写的草率的题解在这里:PKUWC2018 minimax

    Slay the Spire

    注意到强化牌的强化倍数都是大于(1)的正整数,所以可以发现能强化就尽量强化。

    (F(x,y))表示强化牌抽(x)张打出(y)张的倍率之和

    (G(x,y))表示攻击牌抽(x)张打出(y)张的攻击之和

    那么我们枚举抽了多少张攻击牌,在利用以上两个函数就可以算出答案了。

    至于怎么计算那两个函数就看代码把。

    #include<bits/stdc++.h>
    #define rep(i,l,r) for(int i=l;i<=r;i++)
    using namespace std;
    const int sz=3e3+7;
    const int mod=998244353;
    int T;
    int ans;
    int n,m,k;
    int a[sz],b[sz];
    int inv[sz],fac[sz],ifac[sz];
    int sum[sz],f[sz][sz],g[sz][sz];
    void init(){
    	fac[0]=ifac[0]=1;
    	fac[1]=ifac[1]=inv[1]=1;
    	for(int i=2;i<sz;i++){
    		inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    		fac[i]=1ll*i*fac[i-1]%mod;
    		ifac[i]=1ll*inv[i]*ifac[i-1]%mod;
    	}
    }
    int C(int n,int m){
    	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
    }
    int F(int x,int y){
    	//抽出x张强化牌,y张打出去的效用和
    	if(x<y) return 0;
    	if(y==0) return C(n,x);
    	int ret=0;
    	rep(i,x-y+1,n-y+1) 
    		ret=(ret+1ll*f[y][i]*C(i-1,x-y)%mod)%mod;
    	return ret;
    }
    int G(int x,int y){
    	//抽出x张攻击牌,y张打出去的效用和 
    	if(x<y) return 0;
    	if(y==0) return 0;
    	int ret=0;
    	rep(i,x-y+1,n-y+1)
    		ret=(ret+1ll*g[y][i]*C(i-1,x-y)%mod)%mod;
    	return ret;
    }
    int main(){
    	init();
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d%d%d",&n,&m,&k);
    		rep(i,1,n) rep(j,1,n) f[i][j]=g[i][j]=0;
    		rep(i,1,n) scanf("%d",&a[i]);
    		rep(i,1,n) scanf("%d",&b[i]);
    		sort(a+1,a+n+1);
    		sort(b+1,b+n+1);
    		rep(i,1,n){
    			f[1][i]=a[i];
    			sum[i]=(sum[i-1]+f[1][i])%mod;
    		}
    		rep(i,2,n){
    			rep(j,1,n-i+1)
    				f[i][j]=1ll*a[j]*((sum[n]-sum[j]+mod)%mod)%mod;
    			rep(j,1,n)
    				sum[j]=(sum[j-1]+f[i][j])%mod;
    		}
    		rep(i,1,n){
    			g[1][i]=b[i];
    			sum[i]=(sum[i-1]+g[1][i])%mod;
    		}
    		rep(i,2,n){
    			rep(j,1,n-i+1)
    				g[i][j]=(1ll*b[j]*C(n-j,i-1)%mod+(sum[n]-sum[j]+mod)%mod)%mod;
    			rep(j,1,n)
    				sum[j]=(sum[j-1]+g[i][j])%mod;
    		}
    		ans=0;
    		rep(i,max(m-n,0),min(n,m)){
    			int j=m-i;
    			ans=(ans+1ll*F(i,min(i,k-1))*G(j,max(k-i,1))%mod)%mod;
    		}
    		printf("%d
    ",ans);
    	}
    }
    
    

    斗地主

    不可能写的,这辈子都不可能写的。

    随机算法

    枚举不可选择的集合(S)(不可选择的集合为现在已经有的独立集以及和这些独立集相连的点)

    每次,选择一个新的点放入独立集,那么它会新增一些点不可选。

    这些点只要放在放入独立集的新点的后面就可以了,它的贡献就是一个组合数。

    然后就没了。

    #include<bits/stdc++.h>
    using namespace std;
    const int sz=24;
    const int mod=998244353;
    int n,m;
    int u,v,t;
    int link[sz];
    int bit[1<<20];
    int fac[sz],ifac[sz],inv[sz];
    int dp[1<<20],g[1<<20];
    void init(){
    	fac[0]=ifac[0]=1;
    	fac[1]=ifac[1]=inv[1]=1;
    	for(int i=2;i<sz;i++){
    		inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    		fac[i]=1ll*i*fac[i-1]%mod;
    		ifac[i]=1ll*inv[i]*ifac[i-1]%mod;
    	}
    }
    int C(int n,int m){
    	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
    }
    int main(){
    	init();
    	scanf("%d%d",&n,&m);
    	t=1<<n;
    	for(int i=1;i<=n;i++) link[i]|=1<<(i-1);
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&u,&v);
    		link[u]|=1<<(v-1);
    		link[v]|=1<<(u-1);
    	}
    	for(int i=1;i<t-1;i++) bit[i]=bit[i>>1]+(i&1);
    	dp[0]=1,g[0]=0;
    	for(int i=0;i<t;i++){
    		for(int j=1;j<=n;j++) 
    		if(((i>>(j-1))&1)==0){
    			int s=i|link[j],p=s^i;
    			if(g[i]+1<g[s]) continue;
    			else if(g[i]+1==g[s])
    				dp[s]=(dp[s]+1ll*dp[i]*C(n-bit[i]-1,bit[p]-1)%mod*fac[bit[p]-1]%mod)%mod;
    			else{
    				g[s]=g[i]+1;
    				dp[s]=1ll*dp[i]*C(n-bit[i]-1,bit[p]-1)%mod*fac[bit[p]-1]%mod;
    			}
    		}
    	}
    	int ans=1ll*dp[t-1]*ifac[n]%mod;
    	printf("%d
    ",ans);
    }
    

    猎人杀

    你以为我会吗?

    不,我不会。

    随机游走

    (min-max)容斥好题

    之后再(FMT)一下就可以了。

    #include<bits/stdc++.h>
    #define go(x) for(int i=head[x];i;i=edge[i].nxt)
    #define now edge[i].v
    using namespace std;
    const int sz=20;
    const int mod=998244353;
    int S;
    int t;
    int k,x;
    int n,q,rt;
    int u,v,cnt;
    int head[sz];
    int a[sz],b[sz],d[sz];
    int s[1<<20],bit[1<<20];
    struct Edge{
    	int v,nxt;
    }edge[sz<<1];
    int qpow(int x,int y){
    	int ret=1;
    	for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) ret=1ll*x*ret%mod;
    	return ret;
    }
    void add(int u,int v){
    	edge[++cnt]=(Edge){v,head[u]};head[u]=cnt;
    	edge[++cnt]=(Edge){u,head[v]};head[v]=cnt;
    }
    void dfs(int x,int fa){
    	int asum=0,bsum=0;
    	go(x) if(now!=fa){
    		dfs(now,x);
    		asum=(asum+a[now])%mod;
    		bsum=(bsum+b[now])%mod;
    	}
    	if(S>>(x-1)&1) a[x]=b[x]=0;
    	else{
    		int inv=qpow((d[x]-asum+mod)%mod,mod-2);
    		a[x]=inv,b[x]=1ll*inv*(d[x]+bsum)%mod;
    	}
    }
    int main(){
    	scanf("%d%d%d",&n,&q,&rt);
    	for(int i=1;i<n;i++){
    		scanf("%d%d",&u,&v);
    		d[u]++;
    		d[v]++;
    		add(u,v);
    	}
    	t=1<<n;
    	for(S=1;S<t;S++){
    		dfs(rt,0);
    		bit[S]=bit[S>>1]^(S&1);
    		s[S]=bit[S]?b[rt]:(mod-b[rt])%mod;
    	}
    	for(int i=1;i<t;i<<=1)
    		for(int j=0;j<t;j+=2*i)
    			for(int k=0;k<i;k++)
    				s[i+j+k]=(s[i+j+k]+s[j+k])%mod;
    	while(q--){
    		S=0;
    		scanf("%d",&k);
    		while(k--){
    			scanf("%d",&x);
    			S|=1<<(x-1);
    		}
    		printf("%d
    ",s[S]);
    	}
    }
    

    真实排名

    简单题,拿(two-point)随便搞搞就可以了。

    之前写的草率的题解在这里:PKUSC2018 真实排名

    最大前缀和

    似乎又是一个状态压缩的(DP)?

    好像是我很久以前写的(可能还是我给别人胡完让他帮我写的),已经不记得了。

    先咕着。

    主斗地

    不好意思,我是不会写的。

    星际穿越

    不会,咕着。

    神仙的游戏

    (border)变成循环节就可以了,再(FFT)一下就可以了。

    之前写的草率的题解在这里:PKUSC2018 神仙的游戏

    PKUSC

    似乎并不难想。

    只需要将每个点在多边形内的概率算出来再相加就可以得到期望了。

    每个点的贡献大概就是以它到原点的距离作圆,看圆弧有多少在多边形内。

    但是看到隔壁的ATS 大佬肝了快一天还没肝出来,我实在是缺乏勇气。

  • 相关阅读:
    Java 线程
    杂记
    字符流分类详细介绍和各种字符流类介绍与使用 字符集
    内部类 与 匿名内部类
    Java IO流学习总结
    java的集合框架
    jquery 只能投票一次
    C# 简易版的计算器程序
    常用DBHelper类
    jQuery异步提交
  • 原文地址:https://www.cnblogs.com/river-flows-in-you/p/11993539.html
Copyright © 2020-2023  润新知