NOI Online #2 提高组

涂色游戏

题意:

有一个长为\(10^{20}\)的纸带,可以把\(x\)的倍数的位置都涂红,可以把的倍\(y\)数的位置都涂蓝,既是\(x\)倍数又是\(y\)倍数的位置可以任意选颜色,问所有需要涂色的位置,能不能做到没有\(k\)个连续相同的颜色。

\(x,y,k\leq 10^9,T\leq 10^6\)

题解:

不妨设\(x<y\)

那么我们就是想知道相邻两个蓝色之间最多能涂多少个红色。

假设第一个红色距离左端点为\(g\)

那么必须满足\(px-qy=g\)

根据裴蜀定理,\(gcd(x,y)|g\),所以\(g\)\(gcd(x,y)\)时最小。

最大连续数量就是\(\frac{y-1-gcd(x,y)}{x}\)

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
	const int N=3e5+10;
	int n,m,len,v;
	int a[N],s[N];
	inline void main()
	{
		ios::sync_with_stdio(0);
		cin.tie(0),cout.tie(0);
		int skx;cin>>skx;
		while(skx--)
		{
			int x,y,k;cin>>x>>y>>k;
			if(x>y) swap(x,y);
			int tmp=max(y-1-__gcd(x,y),0ll)/x+1;	
			if(tmp>=k) cout<<"NO\n";
			else cout<<"YES\n";
		}
	}
}
signed main()
{
	red::main();
	return 0;
}
/*


*/

子序列问题

题意:

给定数列\(A\)

\(f(l,r)\)表示\(a[l]\sim a[r]\)有多少个不同的数字

\(\sum_{l=1}^{n}\sum_{r=l}^n(f(l,r)^2)\)

\(n\leq 10^6\)

题解:

考虑增量法,假如现在有\(ans[1,1],ans[1,2],……,ans[1,i]\)

考虑假如\(a[i+1]\),会对之前的答案数组有什么影响。

对于\(a[i+1]\)上一次出现的位置\(pos\)和之前来说,答案没有变化。

对于\(pos+1\sim i+1\)这些位置来说,答案由\(d^2->(d+1)^2\)

怎么维护平方改变呢,拆分一下:

\[(x+k)^2=x^2+2*x*k+k^2 \]

两个平方项可以\(O(1)\)得到,中间项可以维护一下区间和。

然后上线段树做区间修改和全局查询。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
	const int N=1e6+10,mod=1e9+7;
	int n,num;
	int a[N],c[N];
	int pre[N];
	inline int sqr(int x){return x*x%mod;}
	struct segment
	{
		int ans[N<<2],ans2[N<<2],tag[N<<2];
		inline void work(int l,int r,int p,int k)
		{
			ans2[p]=(ans2[p]+(r-l+1)*sqr(k)%mod+2*ans[p]*k)%mod;
			ans[p]=(ans[p]+(r-l+1)*k)%mod;
			tag[p]=(tag[p]+k)%mod;
		}
		inline void pushdown(int l,int r,int p)
		{
			work(l,mid,ls(p),tag[p]);
			work(mid+1,r,rs(p),tag[p]);
			tag[p]=0;
		}
		inline void update(int tl,int tr,int l,int r,int p,int k)
		{
			if(tl<=l&&r<=tr)
			{
				work(l,r,p,k);
				return;
			}
			if(tag[p]) pushdown(l,r,p);
			if(tl<=mid) update(tl,tr,l,mid,ls(p),k);
			if(tr>mid) update(tl,tr,mid+1,r,rs(p),k);
			ans[p]=(ans[ls(p)]+ans[rs(p)])%mod;
			ans2[p]=(ans2[ls(p)]+ans2[rs(p)])%mod;
		}
	}T;
	inline void main()
	{
		ios::sync_with_stdio(0);
		cin.tie(0),cout.tie(0);
		cin>>n;
		for(int i=1;i<=n;++i)
		{
			cin>>a[i];
			c[++num]=a[i];
		}
		sort(c+1,c+num+1);
		num=unique(c+1,c+num+1)-c-1;
		int ans=0;
		for(int i=1;i<=n;++i)
		{
			a[i]=lower_bound(c+1,c+num+1,a[i])-c;
			int tmp=pre[a[i]]+1;
			T.update(tmp,i,1,n,1,1);
			ans=(ans+T.ans2[1])%mod;
			pre[a[i]]=i;
			//cout<<ans<<"!!"<<endl;
		}
		cout<<ans<<'\n';
	}
}
signed main()
{
	red::main();
	return 0;
}
/*
1
1 2
1 2 3
1 2 3 3

*/

游戏

题意:

\(n=2m\)个节点的一棵树,每个人有\(m\)个节点,每次每个人在自己的节点里选一个以前没选过的。

平局是说两个人这次选的节点没有祖先关系。

问所有选点方案中,有\(k\)次不平局的方案数。

两种方案不同,当且仅当存在一次,小\(A\)选某一个点时,小\(B\)选了另一个点,和选的顺序没有关系。

\(n\leq 5000\)

题解:

问题是恰好,用二项式反演转化为至少。

如何求至少有\(k\)个不平局的方案数?钦定\(k\)个不平局,剩下的随便排列。

所以先求:钦定\(k\)个不平局,剩下暂时不管的方案数。

这个怎么求呢,树上背包就行了。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
	const int N=5000+10,mod=998244353;
	int n,m;
	int dp[N][N],tmp[N];
	int g[N],f[N];
	int fac[N*5],inv[N*5];
	int str[N],sum[N];
	vector<int> eg[N];
	char col[N];
	inline int fast(int x,int k)
	{
		int ret=1;
		while(k)
		{
			if(k&1) ret=ret*x%mod;
			x=x*x%mod;
			k>>=1;
		}
		return ret;
	}
	inline int C(int n,int m)
	{
		if(n<m) return 0;
		return fac[n]*inv[m]%mod*inv[n-m]%mod;
	}
	inline void init(int n)
	{
		fac[0]=inv[0]=1;
		for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%mod;
		inv[n]=fast(fac[n],mod-2);
		for(int i=n-1;i>=1;--i) inv[i]=inv[i+1]*(i+1)%mod;
	}
	inline void dfs(int now,int fa)
	{
		str[now]=1;
		sum[now]=(col[now]=='1');
		int c=col[now]-'0';
		dp[now][0]=1;
		for(int t:eg[now])
		{
			if(t==fa) continue;
			dfs(t,now);
			for(int i=0;i<=str[now]+str[t];++i) tmp[i]=0;
			for(int i=0;i<=str[now];++i)
			{
				for(int j=0;j<=str[t];++j)
				{
					tmp[i+j]=(tmp[i+j]+dp[now][i]*dp[t][j])%mod;
				}
			}
			for(int i=0;i<=str[now]+str[t];++i) dp[now][i]=tmp[i];
			str[now]+=str[t];
			sum[now]+=sum[t];
		}
		for(int i=str[now];i>=1;--i)
		{
			if(c&&str[now]-sum[now]>=i) dp[now][i]=(dp[now][i]+dp[now][i-1]*(str[now]-sum[now]-i+1))%mod;
			if(!c&&sum[now]>=i) dp[now][i]=(dp[now][i]+dp[now][i-1]*(sum[now]-i+1))%mod;
		}
		//if(now==1) cout<<dp[now][3]<<' '<<sum[now]<<"!"<<endl;
	}
	inline void main()
	{
		ios::sync_with_stdio(0);
		cin.tie(0),cout.tie(0);
		cin>>n;
		cin>>(col+1);
		init(2*n);
		for(int i=1;i<n;++i)
		{
			int x,y;cin>>x>>y;
			eg[x].emplace_back(y);
			eg[y].emplace_back(x);
		}
		dfs(1,0);
		for(int i=0;i<=n/2;++i)
		{
			f[i]=dp[1][i]*fac[n/2-i]%mod;
			//cout<<i<<' '<<dp[1][i]<<' '<<fac[n/2-i]<<' '<<f[i]<<"!!"<<endl;
		}
		for(int i=0;i<=n/2;++i)
		{
			int ans=0;
			for(int j=i,opt=1;j<=n/2;++j,opt=-opt)
			{
				ans=(ans+opt*C(j,i)*f[j])%mod;
			}
			cout<<(ans%mod+mod)%mod<<'\n';
		}
	}
}
signed main()
{
	red::main();
	return 0;
}
/*
1
1 2
1 2 3
1 2 3 3

*/
posted @ 2022-05-12 20:01  lovelyred  阅读(22)  评论(0编辑  收藏  举报