1. 程式人生 > 其它 >NOI Online #2 提高組

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

*/