20190314模拟

**T1 shampagne

玩到一半我就不玩了真是菜成sb

玩一下链的情况,我们发现,只有在大小为222BobBobBob必胜

对于大小为333的情况,显然AlanAlanAlan必胜

对于一个奇链,我们取左边第二个,这时BobBobBob只能取左边第一个,转化为更小的奇链

对于一个偶链,我们取左边第一个,这是BobBobBob只能取左边第二个,转化为一个更小的奇链

那么这个性质,我们可以推广到连通块上

其实感性认识就是每次删掉叶子节点还是叶子节点的父亲的区别?

具体证明如下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
感觉很多博弈题都是这样…

从简单的情况推一个结论,然后推广到更复杂的情况上…

很多推广都感性认识一下??

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=5005;
struct edge{int x,y,next;}a[2*MAXN];int len,last[MAXN];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}

int du[MAXN],n,K;
int pi[MAXN];
bool bk;
void dfs(int x,int fa)
{
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y!=fa)
		{
			dfs(y,x);
			if(pi[y])continue;
			if(pi[x])bk=false;
			pi[x]=pi[y]=1;
		}
	}
}
int main()
{
	freopen("shampagne.in","r",stdin);
	freopen("shampagne.out","w",stdout);
	n=read();K=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		ins(x,y);ins(y,x);
		du[x]++;du[y]++;
	}
	bk=true;
	dfs(1,0);
	for(int i=1;i<=n;i++)bk&=(pi[i]!=0);
	if(!bk||K<(n/2)-1)puts("Alan");
	else puts("Bob");
	return 0;
}

T2 whiskey

开始sb了以为是一个三维体积并

然后,我们可以枚举这个第一维xxx,把第二第三维y,zy,zy,z扔到平面上

那么只需要知道不合法的面积是什么

那么对于一个点,不合法的就是y≤yi,z≤ziy\leq y_i,z \leq z_iyyi,zzi的面积并

我们以yyy作为xxx轴,zzz作为yyy轴,那么一定是一个从左往右高度递减的图形

插入一个点的时候只需要找到前驱后继,讨论加入即可

对于后面的点,我们需要满足y&gt;max(y),z&gt;max(z)y&gt; max(y),z&gt;max(z)y>max(y),z>max(z),那么同理讨论一下就可以得到不合法面积了

线段树维护即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define lc now<<1
#define rc now<<1|1
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=500001;
int n,P,Q,R;
struct segtree
{
	LL mn[MAXN*4],mx[MAXN*4],sum[MAXN*4],lazy[MAXN*4];
	void down(int now,int l,int r)
	{
		if(!lazy[now])return ;
		int mid=(l+r)/2;
		mn[lc]=mx[lc]=lazy[now];mn[rc]=mx[rc]=lazy[now];
		sum[lc]=1LL*(mid-l+1)*mn[lc];
		sum[rc]=1LL*(r-mid)*mn[rc];
		lazy[lc]=lazy[now];lazy[rc]=lazy[now];
		lazy[now]=0;
	}
	int getpos(int now,int l,int r,int u)//找第一个小于他的位置 
	{
		if(mn[now]>u)return -1;
		if(l==r)return l;
		int mid=(l+r)/2;down(now,l,r);
		if(mn[lc]>=u)return getpos(rc,mid+1,r,u);
		else return getpos(lc,l,mid,u);
	}
	int getpos1(int now,int l,int r,int u)
	{
		if(mx[now]<u)return -1;
		if(l==r)return l;
		int mid=(l+r)/2;down(now,l,r);
		if(mx[rc]<=u)return getpos1(lc,l,mid,u);
		else return getpos1(rc,mid+1,r,u);
	}
	void modify(int now,int l,int r,int ql,int qr,int u)
	{
		if(l==ql&&r==qr){mn[now]=mx[now]=lazy[now]=u;sum[now]=1LL*(r-l+1)*u;return ;}
		int mid=(l+r)/2;down(now,l,r);
		if(qr<=mid)modify(lc,l,mid,ql,qr,u);
		else if(mid+1<=ql)modify(rc,mid+1,r,ql,qr,u);
		else modify(lc,l,mid,ql,mid,u),modify(rc,mid+1,r,mid+1,qr,u);
		mn[now]=min(mn[lc],mn[rc]);mx[now]=max(mx[lc],mx[rc]);
		sum[now]=sum[lc]+sum[rc];
	}
	int getright(int now,int l,int r)
	{
		if(mn[now]!=0)return -1;
		if(l==r)return l;
		int mid=(l+r)/2;down(now,l,r);
		if(mn[lc]==0)return getright(lc,l,mid);
		else return getright(rc,mid+1,r);
	}
	LL getcal(int now,int l,int r,int ql,int qr)
	{
		if(l==ql&&r==qr)return sum[now];
		int mid=(l+r)/2;down(now,l,r);
		if(qr<=mid)return getcal(lc,l,mid,ql,qr);
		else if(mid+1<=ql)return getcal(rc,mid+1,r,ql,qr);
		else return getcal(lc,l,mid,ql,mid)+getcal(rc,mid+1,r,mid+1,qr);
	}
}seg;
int mx1[21][MAXN],mx2[21][MAXN],Log[MAXN],bin[25];
struct node{int a,b,c;}w[MAXN];
bool cmp(node n1,node n2){return n1.a<n2.a;}
void init()
{
	bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]<<1;
	for(int i=2;i<MAXN;i++)Log[i]=Log[i>>1]+1;
	for(int i=1;i<=n;i++)mx1[0][i]=w[i].b,mx2[0][i]=w[i].c;
	for(int i=1;bin[i]<=n;i++)
		for(int x=1;x+bin[i]-1<=n;x++)
			mx1[i][x]=max(mx1[i-1][x],mx1[i-1][x+bin[i-1]]),mx2[i][x]=max(mx2[i-1][x],mx2[i-1][x+bin[i-1]]);
}
int getmx(int l,int r,int o)
{
	int K=Log[r-l+1];
	if(o==1)return max(mx1[K][l],mx1[K][r-bin[K]+1]);
	else return max(mx2[K][l],mx2[K][r-bin[K]+1]);
}
int sta[MAXN],tp;
int main()
{
	freopen("whiskey.in","r",stdin);
	freopen("whiskey.out","w",stdout);
	n=read();P=read();Q=read();R=read();
	for(int i=1;i<=n;i++)w[i].a=read(),w[i].b=read(),w[i].c=read();
	sort(w+1,w+1+n,cmp);
	init();
	LL ans=0;
	for(int i=1;i<=n;i++)
	{
		int sss=w[i].a-w[i-1].a;
		LL su=seg.sum[1];
		int u1=getmx(i,n,1),u2=getmx(i,n,2);
		if(u1!=Q&&u2!=R)
		{
			LL ca=seg.getcal(1,1,Q,1,u1);
			su=su-ca+1LL*u1*R;
			int fir=seg.getpos(1,1,Q,u2);
			if(fir!=-1)
			{
				int nw=max(u1+1,fir);
				LL ca=seg.getcal(1,1,Q,nw,Q);
				su=su-ca+1LL*(Q-nw+1)*u2;
			}
		}
		else su=1LL*Q*R;
		ans+=1LL*sss*(1LL*Q*R-su);
		int fir=seg.getpos(1,1,Q,w[i].c);
		if(i==1)seg.modify(1,1,Q,1,w[i].b,w[i].c);
		else
		{
			if(fir==-1)//没有比我小的
			{
				int u=seg.getright(1,1,Q);
				if(u==-1)continue;
				if(u<=w[i].b)seg.modify(1,1,Q,u,w[i].b,w[i].c);
			}
			else
			{
				int lst=fir-1;
				if(lst<1)seg.modify(1,1,Q,1,w[i].b,w[i].c);
				else if(w[i].b>lst)seg.modify(1,1,Q,lst+1,w[i].b,w[i].c);
			}
		}
		
//		printf("CHECKER\n");
////		pr2(ans);
//		printf("ans=%lld\n",ans);
//		for(int i=1;i<=Q;i++)pr1(seg.getcal(1,1,Q,i,i));
//		puts("");
	}
	ans+=1LL*(1LL*Q*R-seg.sum[1])*(P-w[n].a);
	pr2(ans);
}

**T3 vodka

好题

以为是OSUOSUOSU的加强版…然后想着怎么维护期望的次方与次方的期望的关系去了…

设一个xi,jx_{i,j}xi,j表示第iii个数在第jjj次是否被拿出来了

那么要求的就是

E((x1,1+x1,2+...+x1,n)F∗...∗(xL,1+xL,2+...+XL,n)F)E((x_{1,1}+x_{1,2}+...+x_{1,n})^F*...*(x_{L,1}+x_{L,2}+...+X_{L,n})^F)E((x1,1+x1,2+...+x1,n)F...(xL,1+xL,2+...+XL,n)F)

发现每一次选出任一个数的概率都是固定的,那么考虑用总数/方案数来算期望

把里面的式子拆一拆,那么可以得到内部用∗*连接的单项式,所以我们只需要求出下式期望的和

(x1,a[1,1]∗x1,a[1,2]∗...∗x1,a[1,F])∗...∗(xL,a[L,1]∗xL,a[L,2]∗...∗xL,a[L,F])(x_{1,a[1,1]}*x_{1,a[1,2]}*...*x_{1,a[1,F]})*...*(x_{L,a[L,1]}*x_{L,a[L,2]}*...*x_{L,a[L,F]})(x1,a[1,1]x1,a[1,2]...x1,a[1,F])...(xL,a[L,1]xL,a[L,2]...xL,a[L,F])

其中a[i,j]a[i,j]a[i,j]表示在第iii个括号中,取的第jjj个位置是哪个位置

即代表了我们要在第a[i][j]a[i][j]a[i][j]次取值时取出iii这个值

我们将其写成一个L∗FL*FLF的矩阵形式,显然的,任意两行不能有数相同。因为有数相同时一定有一个xxx000,那么此时贡献就为000了。考虑其概率,假如矩阵中有TTT个相同的值,那么其概率为1KT\frac{1}{K^T}KT1,因为我们已经固定了KKK个位置是选择哪些数的

至此可以转化题意,在L∗FL*FLF的矩形中填入[1,n][1,n][1,n]的数,要求任意两行没有数相同,问填入的数的不同种类为TTT时的方案数

逐行dpdpdp,设f[i][j]f[i][j]f[i][j]为前iii个格子放了jjj种数,那么转移有f[i][j]=f[i−1][j−1]+f[i−1][j]∗jf[i][j]=f[i-1][j-1]+f[i-1][j]*jf[i][j]=f[i1][j1]+f[i1][j]j

对于一行结果f[i][j]f[i][j]f[i][j],我们最后乘一个j!j!j!就可以得到总方案数,即整体更换任意种数的位置

那么再做LLL次背包dpdpdp,求出总共dp[i]dp[i]dp[i]表示放了iii种数的方案数,最后贡献即为

∑Cni∗i!∗1Ki∗dp[i]\sum C_n^i*i!*\frac{1}{K^i}*dp[i]Cnii!Ki1dp[i]

dpdpdp上界可能去到nnn?,我们发现模数是200320032003而前部分在i&gt;=2003i&gt;=2003i>=2003时已经为000了,所以只需要保存前mod−1mod-1mod1个位置的值即可

上面的背包dpdpdp可以倍增优化,还可以加FFTFFTFFT进一步优化。使用倍增优化后复杂度即为O(mod2logL+mod2)O(mod^2logL+mod^2)O(mod2logL+mod2)

感觉,真的是一个很妙的题啊…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=2005;
const int mod=2003;
int pow_mod(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1)ret=1LL*ret*a%mod;
		a=1LL*a*a%mod;b>>=1;
	}
	return ret;
}
void ad(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int dp[MAXN][MAXN],inv[MAXN],n,K,L,F;
int C(int n,int m)
{
	int ret=1;
	for(int i=1;i<=m;i++)ret=1LL*ret*inv[i]%mod*(n-i+1)%mod;
	return ret;
}
int f[MAXN];
int gets(int u)
{
	int re=0;
	while(u)re++,u>>=1;
	return re;
}
int pre[MAXN],inve[MAXN];
int main()
{
	freopen("vodka.in","r",stdin);
	freopen("vodka.out","w",stdout);
	pre[0]=1;
	for(int i=1;i<MAXN;i++)pre[i]=pre[i-1]*i%mod;
	inve[mod-1]=pow_mod(pre[mod-1],mod-2);
	for(int i=mod-2;i>=0;i--)inve[i]=inve[i+1]*(i+1)%mod;
	inv[0]=1;
	for(int i=1;i<MAXN;i++)inv[i]=pow_mod(i,mod-2);
	n=read();K=read();L=read();F=read();
	dp[0][0]=1;
	for(int i=1;i<MAXN;i++)
		for(int j=1;j<=i;j++)dp[i][j]=(dp[i-1][j-1]+dp[i-1][j]*j)%mod;
	f[0]=1;
	int len=gets(L);
	for(int i=len;i>=1;i--)
	{
		if(i!=len)
		{
			for(int j=mod;j>=0;j--)
			{
				int temp=0;
				for(int k=0;k<j;k++)if(f[k])ad(temp,f[k]*f[j-k]%mod);
				f[j]=temp;
			}
		}
		if(L&(1<<(i-1)))
		{
			for(int j=mod;j>=0;j--)
			{
				int temp=0;
				for(int k=0;k<j;k++)if(f[k])ad(temp,f[k]*dp[F][j-k]%mod);
				f[j]=temp;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=min(n,mod);i++)
		ad(ans,1LL*C(n,i)*pre[i]%mod*pow_mod(pow_mod(K,i),mod-2)%mod*f[i]%mod);
	pr2(ans);
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值