NOI Online #2 提高组

涂色游戏#

题意:

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

x,y,k109,T106

题解:

不妨设x<y

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

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

那么必须满足pxqy=g

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

最大连续数量就是y1gcd(x,y)x

Copy
#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]a[r]有多少个不同的数字

l=1nr=ln(f(l,r)2)

n106

题解:

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

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

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

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

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

(x+k)2=x2+2xk+k2

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

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

Copy
#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选了另一个点,和选的顺序没有关系。

n5000

题解:

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

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

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

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

Copy
#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 @   lovelyred  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
CONTENTS