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\)
怎么维护平方改变呢,拆分一下:
两个平方项可以\(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
*/