这是题目描述的链接: http://lifecraft-mc.com/wp-content/uploads/2018/03/problems1.pdf
(虽然这次没去清北,但还是厚颜无耻的做了一下这套题)
T1:
估计noip水平的选手都知道这个题可以用矩阵乘法水过去,但是作为一个还有8天就要省选的蒟蒻,我来提供一种娱乐做法:指数型生成函数。 像1和3这样必须出现且出现偶数次的数就可以用一个闭形式为 (e^x + e^-x)/2 - 1 的指数型生成函数表示,具体的推式子见代码。
/*
ANS = (e^x)^3 * ((e^x + e^-x)/2 -1)^2
= e^3x * ((e^2x + 2 + e^-2x)/4 - (e^x + e^-x) + 1)
= (e^5x + e^x + 2*e^3x)/4 - (e^4x + e^2x) +e^3x
= (5^x + 2*3^x + 1)/4 - (4^x + 2^x) + 3^x
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<vector>
#include<cstdlib>
#define ll long long
using namespace std;
const int ha=1000000007;
const int inv=ha/2+1;
int ans=0;
ll n;
inline int add(int x,int y){
x+=y;
return x>=ha?x-ha:x;
}
inline int DOUBLE(int x){
return add(x,x);
}
inline int ksm(int x,int y){
int an=1;
for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
return an;
}
int main(){
// freopen("number.in","r",stdin);
// freopen("number.out","w",stdout);
scanf("%lld",&n),n%=(ha-1);
ans=add(add(ksm(5,n),1),DOUBLE(ksm(3,n)));
ans=ans*(ll)inv%ha*(ll)inv%ha;
ans=add(ans,ha-add(ksm(2,n),ksm(4,n)));
ans=add(ans,ksm(3,n));
printf("%d
",ans);
return 0;
}
T2:
这也是一道比较基础的组合计数了2333,首先当我们确定多少行被选了奇数次的时候,多少列被选奇数次也就确定了(除了被选奇数次的行数*2==n 这种特殊情况),所以我们可以直接大力枚举选了多少奇行,然后乘上一些组合系数即可。
/*
设最后有x行y列被选了奇数次,那么满足:
x*m + (n-2x)*y =k
并且 (r-x) 和 (c-y) 都必须是偶数。
然后此时的方案数就是:
C(n,x) * C(m,y) * C(n+(r-x)/2-1,(r-x)/2) * C(m+(c-y)/2-1,(c-y)/2)
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<vector>
#include<cstdlib>
#define ll long long
using namespace std;
const int ha=1000000007;
const int maxn=300000;
int n,m,r,c,ans=0;
int jc[maxn+5],ni[maxn+5];
ll K;
inline int add(int x,int y){
x+=y;
return x>=ha?x-ha:x;
}
inline int ksm(int x,int y){
int an=1;
for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
return an;
}
inline void init(){
jc[0]=1;
for(int i=1;i<=maxn;i++) jc[i]=jc[i-1]*(ll)i%ha;
ni[maxn]=ksm(jc[maxn],ha-2);
for(int i=maxn;i;i--) ni[i-1]=ni[i]*(ll)i%ha;
}
inline int C(int x,int y){
return jc[x]*(ll)ni[y]%ha*(ll)ni[x-y]%ha;
}
inline void solve(){
ll lef,O,y;
for(int i=r;i>=0;i-=2){
if(i>n) continue;
lef=K-i*(ll)m;
O=(n-2*i);
if(!O){
if(lef) continue;
int now=C(n,i)*(ll)C(m+c-1,c)%ha;
now=now*(ll)C(n+((r-i)>>1)-1,(r-i)>>1)%ha;
ans=add(ans,now);
continue;
}
if(lef%O) continue;
y=lef/O;
if(y<0||y>c||y>m||((c-y)&1)) continue;
int now=C(n,i)*(ll)C(m,y)%ha;
now=now*(ll)C(n+((r-i)>>1)-1,(r-i)>>1)%ha;
now=now*(ll)C(m+((c-y)>>1)-1,(c-y)>>1)%ha;
ans=add(ans,now);
}
}
int main(){
init();
scanf("%d%d%d%d%lld",&n,&m,&r,&c,&K);
solve();
printf("%d
",ans);
return 0;
}
T3:
一道比较好的扫描线题。
我们知道当k==1的时候,这就是一个区间 mex 查询 (只不过0不被统计),而这个是一个经典扫描线问题。
我们把那种方法扩展一下,先预处理出[1,i] 的所有答案,然后考虑左端点向右移动1个单位所带来的影响。当然这个也是可以预处理的,只不过完全可以不用后缀可持久化线段树去预处理(这是我一开始的想法),直接用一个set<>记录 i,i+1,,,i+k-1 这些数出现的最近的一个就可以了。虽然处理一个点的复杂度都是 O(k * log)的,但是set是O(k * log k)的,而后缀可持久化线段树查找是O(k *log n)的,而本题k远小于n,所以emmmmmm
/*
xjb扫描线即可
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<vector>
#include<cstdlib>
#include<set>
#define ll long long
using namespace std;
const int maxn=200105;
set<int> s;
struct ask{
int L,R,num;
bool operator <(const ask &u)const{
return L<u.L;
}
}q[maxn];
int n,m,Q,k,ans[maxn],*pos,w,T[maxn];
int a[maxn],las[maxn],le,ri,ops;
int mn[maxn<<2|1],to[maxn][12];
bool v[maxn];
inline void prework(){
ops=1,v[n+1]=1;
for(int i=1,j;i<=m;i++){
v[a[i]]=1;
for(;ops<=n;ops++){
for(j=0;j<k;j++) if(v[ops+j]) break;
if(j==k) break;
}
T[i]=ops;
}
for(int i=m;i;las[a[i]]=i,i--){
s.clear();
for(int j=0;j<k;j++) s.insert(las[a[i]+j]);
for(int j=0;j<k&&a[i]-j>0;s.erase(las[a[i]+k-j-1]),j++,s.insert(las[a[i]-j])){
to[i][j]=*s.lower_bound(0);
}
}
}
void update(int o,int l,int r){
if(l>=le&&r<=ri){
mn[o]=min(mn[o],w);
return;
}
int mid=l+r>>1,lc=o<<1,rc=(o<<1)|1;
if(le<=mid) update(lc,l,mid);
if(ri>mid) update(rc,mid+1,r);
}
void query(int o,int l,int r){
*pos=min(*pos,mn[o]);
if(l==r) return;
int mid=l+r>>1,lc=o<<1,rc=(o<<1)|1;
if(le<=mid) query(lc,l,mid);
else query(rc,mid+1,r);
}
inline void add(int U){
for(int j=0;j<k&&a[U]-j>0;j++){
le=U+1,ri=to[U][j]-1,w=a[U]-j;
if(le<=ri) update(1,1,n);
}
}
inline void solve(){
for(int i=1;i<=Q;i++){
scanf("%d%d",&q[i].L,&q[i].R);
q[i].num=i;
}
sort(q+1,q+Q+1);
int now=1;
for(int i=1;i<=Q;i++){
while(now<q[i].L) add(now),now++;
pos=ans+q[i].num;
*pos=T[q[i].R];
le=q[i].R;
query(1,1,m);
}
}
inline void output(){
for(int i=1;i<=Q;i++){
if(ans[i]<=n) printf("%d
",ans[i]);
else puts("-1");
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&Q,&k);
for(int i=1;i<=m;i++) scanf("%d",a+i);
fill(las+1,las+n+1,m+1);
memset(mn,0x7f,sizeof(mn));
prework();
solve();
output();
return 0;
}