一月の题

麻了一月都快结束了题也做了一些了却没写这个,更十倍了。

[ABC154F] Many Many Paths

组合数,挺简单
先鸽了

CF1542D

dp,转换一下题意,我们珂以看作是对于每一个 \(+x\) 有多少个子序列能加上他的贡献。我们考虑一个 \(+\) 的贡献能被加上要满足有比它小的 \(+\) 操作,那么我们珂以不管操作的具体大小而是关心它们的大小关系。我们先枚举考虑的第 \(p\) 个操作 \(+x\),设 \(dp[i][j]\) 表示前 \(i\) 个操作中有 \(j\) 个加操作比 \(x\) 小时的子序列数,然后珂以分类讨论转移。

AT_tenka1_2017_f

这个数论题洛谷写了题解了自己看,还有些不懂的记得补。

P5505

直接求合法方案数求不出来,考虑转换成补集,用总方案数(盒子可以有空的)减去不合法方案数(盒子一定有空的)。总方案数简单,就是 \(\prod\limits_{i=\mathtt{1}}^m C\binom{n-\mathtt{1}}{n+a_i-\mathtt{1}}\)。然后分开考虑当有 \(\mathtt{1},\mathtt{2},\ldots,n\) 个盒子为空时对总方案数的贡献。设当前空的盒子有 \(x\) 个,相当于把球放进 \(n-x\) 个盒子中,只算这个 \(x\) 的贡献就是 \(C\binom{n-x-\mathtt{1}}{n-x+a_i-\mathtt{1}}\),根据乘法原理并且盒子是是有标号的,那么有 \(i\) 个盒子为空时的总贡献就是 \(C\binom{i}{n}\times\prod\limits_{j=\mathtt{1}}^m C\binom{n-i-\mathtt{1}}{n-i+a_j-\mathtt{1}}\),注意这里是不能直接总的减去前面式子的,因为大的 \(x\) 的贡献中含有 小的 \(x\) 的贡献,你相当于重复减去了很多 \(i\) 的贡献。正确的做法是考虑容斥,设空盒子有 \(x\) 个时的贡献为 \(f_x\),答案 \(ans=f_{\mathtt{0}}-f_{\mathtt{1}}+f_{\mathtt{2}}-\ldots+(\mathtt{-1})^{n-\mathtt{1}}\times f_{n-\mathtt{1}}\),即 \(\sum\limits_{i=\mathtt{0}}^{n-\mathtt{1}}(\mathtt{-1})^i \times C\binom{i}{n}\times\prod\limits_{j=\mathtt{1}}^m C\binom{n-i-\mathtt{1}}{n-i+a_j-\mathtt{1}}\)
还有需要预处理组合数。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=114514,M=1919810,mod=1e9+7;
ll n,m,a[N],inv[N];
ll qpow(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
ll C(ll n,ll m){
	ll ans=1,res=1;
	for(int i=m+1;i<=n;++i) ans=ans*i%mod;
	for(int i=1;i<=n-m;++i) res=res*i%mod;
	return ans*qpow(res,mod-2)%mod;
}
ll ans,res,f[2005][2005];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;++i) cin>>a[i];
	for(int i=0;i<=2000;++i) f[i][0]=f[i][i]=1;
	for(int i=1;i<=2000;++i)
		for(int j=1;j<=i;++j)
			f[i][j]=(f[i-1][j-1]+f[i-1][j])%mod;
	for(int i=0;i<n;++i){
		res=qpow(-1,i)*f[n][i]%mod;
		for(int j=1;j<=m;++j)
			res=res*f[n-i+a[j]-1][n-i-1]%mod;
		ans=((ans+res)%mod+mod)%mod;
	}
	cout<<ans;
	return 0;
} 

P4113

数据范围 \(\mathtt{2e6}\),那么莫队还是死了,需要严格 \(\mathtt{log}\) 的做法。首先把询问按 \(r\) 排序离线搞没问题,然后我们考虑每个点对后面和他颜色相同的点的贡献,第一次出现这个颜色 \(c\) 时对答案无贡献,第二次才有,所以我们考虑记录每个点 前一个和他颜色相同的点 和 前一个点的前一个点,这样子类似于上一道题,我们用树状数组维护贡献,update(las[i],1),update(las[las[i]],-1),推理过程也和上一道题一样,注意可能会对 \(\mathtt{0}\) 加减贡献,要直接 return。
注:上一道题是P1972,数据结构做题记录里有。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=114514,M=2*1919810;
ll n,cl,m,a[M],ans[M];
ll las[M],lasc[M];
ll c[M];
ll lowbit(ll x){return x&-x;}
void update(ll x,ll k){
	if(!x) return;
	while(x<=n){
		c[x]+=k;
		x+=lowbit(x);
	}
}
ll query(ll x){
	if(!x) return 0;
	ll ans=0;
	while(x){
		ans+=c[x];
		x-=lowbit(x);
	}
	return ans;
}
struct que{
	ll l,r,id;
}q[M];
bool cmp(que x,que y){
	return x.r<y.r;
}
//这个维护挺智慧的 
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>cl>>m;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		las[i]=lasc[a[i]];
		lasc[a[i]]=i;
	}
	for(int i=1;i<=m;++i) cin>>q[i].l>>q[i].r,q[i].id=i;
	sort(q+1,q+m+1,cmp);
	ll pos=1;
	for(int i=1;i<=n;++i){
		update(las[i],1),update(las[las[i]],-1);
		while(q[pos].r==i&&pos<=m){
			ans[q[pos].id]=query(q[pos].r)-query(q[pos].l-1);
			++pos;
		}
	}
	for(int i=1;i<=m;++i) cout<<ans[i]<<'\n';
	return 0;
}
posted @ 2024-01-24 21:40  和蜀玩  阅读(16)  评论(0)    收藏  举报