清北省选 DAY last 集锦

这是题目描述的链接: 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\n",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\n",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\n",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;
} 

  

posted @ 2018-03-29 18:02  蒟蒻JHY  阅读(232)  评论(0编辑  收藏  举报