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
*/