模拟赛20200331【贪心+精度忽略,竞赛图,DP】

T1:小C饮水记
题目描述:

在这里插入图片描述
w i ≤ 1 0 5 , n ≤ 1 0 6 w_i\le10^5,n\le10^6 wi105,n106

题解:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Code:

#include<bits/stdc++.h>
#define maxn 1000005
#define S 40
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,w[maxn],id[maxn],L[maxn],R[maxn];
double pw[S+5],ans;
bool cmp(int i,int j){return w[i]<w[j];}
int main()
{
	freopen("drink.in","r",stdin);
	freopen("drink.out","w",stdout);
	read(n);
	for(int i=1;i<=n;i++) read(w[i]),L[i]=i-1,R[i]=i+1,id[i]=i;
	sort(id+1,id+1+n,cmp),pw[0]=1;
	for(int i=1;i<=S;i++) pw[i]=pw[i-1]*0.5;
	for(int o=1;o<=n;o++){
		int i=id[o];
		double s1=0,s2=0;
		for(int j=1,l=i,r=i;j<=S&&(l||r<=n);j++){
			if(l) s1+=pw[j]*(l-L[l]),l=L[l];
			if(r<=n) s2+=pw[j]*(R[r]-r),r=R[r];
		}
		ans+=2*w[i]*s1*s2;
		L[R[i]]=L[i],R[L[i]]=R[i];
	}
	printf("%.10f\n",ans/n/n);
}

T2:小C的锦标赛
题目描述:

在这里插入图片描述
3 ≤ k ≤ n ≤ 1 0 5 3\le k\le n\le10^5 3kn105

题解:
1.     k = 3 1.~~~k=3 1.   k=3

如果把竞赛图看做无向图,那么它是一个弦图(任意大于3的环中必然有边把环剖开)。所以如果存在一个 > 3 >3 >3的环,那么一定存在一个更小的环。所以如果没有为 3 3 3的简单环,那么就一定是一个DAG。
在这里插入图片描述
这是竞赛图的一大特征:无环竞赛图的拓扑序是唯一确定的,且拓扑序在前的强连通分量内的所有点都向拓扑序在后的所有点有连边
所以DAG的数量就是 n ! n! n!

2.     k = n 2.~~~k=n 2.   k=n

在这里插入图片描述

3.     100 % 3.~~~100\% 3.   100%

在这里插入图片描述
在这里插入图片描述
F = ∑ i = 1 k − 1 f ( i ) i ! F=\sum_{i=1}^{k-1}{f(i)\over i!} F=i=1k1i!f(i) S = ∑ i = 0 n s ( i ) i ! S=\sum_{i=0}^n{s(i)\over i!} S=i=0ni!s(i),那么 S = S ∗ F + 1 S=S*F+1 S=SF+1,即 S = 1 1 − F S={1\over 1-F} S=1F1

复杂度 O ( k l o g k + n l o g n ) O(klogk+nlogn) O(klogk+nlogn).
实际上第二步还可以优化,把 f ( i ) i ! f(i)\over i! i!f(i)看做常系数,这就是一个常系数线性递推,可以通过多项式取模优化至 O ( k l o g k l o g n ) O(klogklogn) O(klogklogn),具体见这里

Code:

#include<bits/stdc++.h>
#define maxn 300005
#define rs(x) resize(x)
using namespace std;
const int mod = 998244353;
int n,k,inv[maxn],g[maxn];
typedef vector<int> Poly;
int w[maxn]={1},r[maxn];
template<class T>inline int Pow(int a,T b){
	int s=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
	return s;
}
void NTT(Poly &a,int len,int flg){
	for(int i=0;i<len;i++) if(i<r[i]) swap(a[i],a[r[i]]);
	for(int i=2,l=1,G=flg>0?3:(mod+1)/3;i<=len;l=i,i<<=1){
		for(int k=l-2,wn=Pow(G,(mod-1)/i);k>=0;k-=2) w[k+1]=1ll*(w[k]=w[k>>1])*wn%mod;
		for(int j=0;j<len;j+=i)
			for(int k=j;k<j+l;k++){
				int u=a[k],v=1ll*w[k-j]*a[k+l]%mod;
				a[k]=(u+v>=mod?u+v-mod:u+v),a[k+l]=(u-v<0?u-v+mod:u-v);
			}
	}
	if(flg==-1) for(int i=0,Iv=Pow(len,mod-2);i<len;i++) a[i]=1ll*a[i]*Iv%mod;
}
Poly Poly_Inv(Poly A,int n){
	Poly B(1,Pow(A[0],mod-2)),C;
	for(int k=2,len=4;k<n<<1;k=len,len<<=1){
		C=Poly(A.begin(),A.begin()+min(n,k));
		for(int i=0;i<len;i++) r[i]=r[i>>1]>>1|(i&1?len>>1:0);
		B.rs(len),C.rs(len),NTT(B,len,1),NTT(C,len,1);
		for(int i=0;i<len;i++) B[i]=1ll*B[i]*(2-1ll*C[i]*B[i]%mod+mod)%mod;
		NTT(B,len,-1),B.rs(min(n,k));
	}
	return B;
}
int main()
{
	freopen("tournament.in","r",stdin);
	freopen("tournament.out","w",stdout);
	scanf("%d%d",&n,&k);
	inv[0]=inv[1]=g[1]=1;
	for(int i=2;i<=n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod,g[i]=Pow(2,1ll*i*(i-1)/2);
	for(int i=2;i<=n;i++) inv[i]=1ll*inv[i]*inv[i-1]%mod;
	Poly G(k); G[0]=1;
	for(int i=1;i<k;i++) G[i]=1ll*g[i]*inv[i]%mod;
	G=Poly_Inv(G,k),G.rs(n+1);
	printf("%d\n",(g[n]-1ll*Poly_Inv(G,n+1)[n]*Pow(inv[n],mod-2)%mod+mod)%mod);
}

T3:小C的线段树
题目描述:

在这里插入图片描述

题解:

注意到 n > m n>m n>m 时,不存在合法的操作序列,答案为0.又因为有 n m ≤ 1 0 5 nm ≤ 10^5 nm105的条件,可以认为 n < 320 n<320 n<320.
那么分开考虑区间每个位置的贡献。 n n n个区间,相当于 n n n个左端点和右端点, i i i这个位置的值就是左边左端点的个数减去右端点的个数。
那么用两个数组一起DP,一个记录方案数,一个记录贡献和
在这里插入图片描述
(第一发写的时候每次把两个数组都memset了…然而 m m m可以很大…暴毙,要直接for循环清零)
Code:

#include<bits/stdc++.h>
#define maxn 335
using namespace std;
const int mod = 998244353;
int n,m,k,f[2][maxn][maxn],g[2][maxn][maxn],S[maxn];
inline int Pow(int a,int b){
	int s=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
	return s;
}
inline void add(int& x,int y){(x+=y)>=mod&&(x-=mod);}
inline void upd(int a[][maxn],int i,int j,int x){
	add(a[i][j],x);
	add(a[i+1][j],x);
	add(a[i][j+1],x);
	add(a[i+1][j+1],x);
}
int main()
{
	freopen("segment.in","r",stdin);
	freopen("segment.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	if(n>=m) {puts("0");return 0;}
	for(int i=1;i<=n;i++) S[i]=Pow(i,k);
	int now=0; f[0][0][0]=1;
	for(int k=1;k<=m;k++,now=!now){
		for(int i=0;i<=n;i++)
			for(int j=0;j<=i;j++)
				f[!now][i][j]=g[!now][i][j]=0;
		for(int i=0;i<=n;i++)
			for(int j=0;j<=i;j++) if(f[now][i][j])
				upd(f[!now],i,j,f[now][i][j]);
		for(int i=0;i<=n;i++)
			for(int j=0;j<=i;j++){
				g[!now][i][j]=(g[!now][i][j]+1ll*f[!now][i][j]*S[i-j])%mod;
				if(g[now][i][j]) upd(g[!now],i,j,g[now][i][j]);
			}
	}
	printf("%d\n",g[now][n][n]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值