S-2涂色问题
CF1260C —— 双倍经验
一眼瞪出结论(当然细节挂了)
就比较(k)和(frac {p_2-2}{p_1} +1)的大小
(k=0)的时候显然是“NO” ——我忘了
而且可能不互质,所以要搞个gcd ——模了几个样例才想起来
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1000005;
inline int read() {
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int p1,p2,k;
int gcd(int a,int b) {
return b?gcd(b,a%b):a;
}
int main() {
// freopen("color.in","r",stdin);
// freopen("color.out","w",stdout);
int T=read();
while(T--) {
p1=read();p2=read();k=read();
if(p1>p2) swap(p1,p2);
int g=gcd(p1,p2);
p1/=g;p2/=g;
int ans=p2%p1,res;
if(ans==0) res=p2/p1-1;
else if(ans==1) res=p2/p1;
else res=p2/p1+1;
if(k==1) puts("NO");
else if(k>res) puts("YES");
else puts("NO");
}
return 0;
}
S-2 子序列问题
复述一下题解
https://www.luogu.com.cn/blog/duyi/solution-p6477
首先看到 (a[i])的范围,离散化
朴素是枚举左右端点,再用set维护集合元素个数,瓶颈在于枚举端点。
正解是考虑每个(a[i])的贡献区间,设(a[i])上次出现的位置为(pre[i]),那么贡献区间即为 ([ (pre[i],i] , [i,n] ])
接下来考虑平方的问题 ————经典套路
平方看成在这些位置中,任选出两个位置(可以重复)的方案数!
设它们分别为((i,j))(不妨设((ileq j)))。则一对((i,j))会对多少个区间产生贡献?不难发现,数量是:
((i-max( ext{pre}[i], ext{pre}[j]))cdot (n-j+1)quad (i> ext{pre}[j]))
暴力枚举 ((i,j))复杂度是(O(n^2))的。考虑优化
如何快速求出:(sum_{i=1}^{j}max( ext{pre}[i], ext{pre}[j]))。我们用一个变量,记录(j)前面所有( ext{pre}[i])之和,则只需要把(leq ext{pre})的这部分值减去,再加上相同数量的( ext{pre}[j])即可。可以用两个树状数组,都以( ext{pre})值为下标,分别维护( ext{pre})值小于(x)的( ext{pre})值之和,及其数量。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1000005;
const int P=1e9+7;
inline int read() {
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
ll s[N],num[N];//树状数组
int n,a[N],b[N],mp[N],pre[N];
inline void upd(int x,int v) {for(int i=x;i<=n;i+=i&-i)s[i]+=x*v,num[i]+=v;}
inline ll sum(int x){ll res=0;for(int i=x;i;i-=i&-i)res+=num[i]*(x+1)-s[i];return res;}
int main() {
n=read();
for(int i=1;i<=n;i++) a[i]=b[i]=read();
sort(b+1,b+1+n);
int tot=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++) {
a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
pre[i]=mp[a[i]];
mp[a[i]]=i;
}
ll cnt=0,ans=0;
for(int i=1;i<=n;i++) {
cnt=(cnt+i-pre[i]+2*(sum(i)-sum(pre[i]))%P+P)%P;
ans=(ans+cnt)%P;
upd(pre[i]+1,1);upd(i+1,-1);
}
printf("%lld
",(ans%P+P)%P);
return 0;
}
J-2 建设城市
组合数学
组合弱者——推+写 近1小时
分类讨论
-
(x,y)同侧
把 ([x,y])看成一个球 ,就是n-(y-x) 个球放到m个盒子里 (可以空)
剩余 n 个球,放到m个盒子里 (可以空) -
异侧
剩余 n-x个球,放到m-i+1个盒子里(可以空)
剩余 y-1个球,放到m-i+1个盒子里(可以空)
剩余 n-y个球,放到i个盒子里(可以空)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=200005;
const int P=998244353;
inline int read() {
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int m,n,x,y;
ll fac[N],inv[N];
ll qpow(ll a,ll b) {
ll res=1;
while(b) {
if(b&1) res=res*a%P;
a=a*a%P;
b>>=1;
}
return res;
}
inline ll C(int n,int m) {
if(n<0||n<m||m<0) return 1;
return fac[n]*inv[m]%P*inv[n-m]%P;
}
ll ans;
int main() {
m=read();n=read();x=read();y=read();
fac[0]=1;
for(int i=1;i<=n+m;i++)
fac[i]=fac[i-1]*i%P;
inv[n+m]=qpow(fac[n+m],P-2);
for(int i=n+m;i;i--) inv[i-1]=inv[i]*i%P;
if(x>y) swap(x,y);
if(x<=n&&y>n) {//异侧
y-=n;
for(int i=1;i<=m;i++)
ans=(ans+C(x-2+i,i-1)*C(n-x+m-i,m-i)%P*C(y-1+m-i,m-i)%P*C(n-y+i-1,i-1)%P)%P;
printf("%lld
",ans);
}
else { //同侧
ans=C(n-(y-x)+m-1,m-1)*C(n+m-1,m-1)%P%P;
printf("%lld
",ans);
}
return 0;
}