20161024的考试】noip模拟,dp,区间中位数,值域分块

T1

题意:1~n的全排列中,有k个逆序对的排列有多少种(n<=1000

思路:显然是个dp,n^3,于是前缀和优化成n^2

#include<bits/stdc++.h>
#define MAXN 1005
#define MOD 10000
using namespace std;	int T,n,K;
int f[MAXN][MAXN];
int sum[MAXN][MAXN];

int main(){
	freopen("permut.in","r",stdin);
	freopen("permut.out","w",stdout);
	
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&K);
		memset(f,0,sizeof f);
		memset(sum,0,sizeof sum);
		f[0][0]=1;
		sum[0][0]=1;
		for(int i=1;i<=K;++i)	sum[0][i]=1;
		for(int i=1;i<=n;++i){
			for(int j=0;j<=K;++j){
				f[i][j]=sum[i-1][j];
				if(j-i>=0)	f[i][j]-=sum[i-1][j-i];
				if(f[i][j]<0)	f[i][j]+=MOD;
			}
			sum[i][0]=1;
			for(int j=0;j<=K;++j)	sum[i][j]=(sum[i][j-1]+f[i][j])%MOD;
		}
		printf("%d\n",f[n][K]%MOD);
	}
	return 0;
}




T2

题意:Q次询问,f[i]表示包含a[i]且a[i]为中位数的区间的长度最长是多少(每次询问的是一段连续的f[i]中的最大值)n=2e3,q=1e5

思路:先n^2预处理一下,求出每个a[i]的f[i],求某个数是不是中位数,可以把比它小的赋值为-1,大的赋值为1,相等的就是0,然后前缀和胡搞乱搞。如果要保证这个区间包含当前这个数,可以从当前位往前往后分别枚举,使这个数前面的和加上这个数后面的正好为0。对于每个询问O(n)地查询就好(反正数据范围这么小就懒得写线段树什么的了23333)

【明明做过中位数图然而今天死磕T3去了【跪地】……不过这道题的题面也有毒2333一眼看上去感觉要在线乱搞23333瞬间觉得要GG】

【不过似乎也差不多忘了这个套路了【躺平】

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MAXN 2005
using namespace std;	int n,Q;
int a[MAXN];
int L[MAXN<<1],R[MAXN<<1];

int f[MAXN];

void init(){
	for(int i=1;i<=n;++i){
		memset(L,-1,sizeof L);
		memset(R,-1,sizeof R);
		L[n]=R[n]=0;
		int cnt=0;
		for(int j=i-1;j;--j){
			if(a[j]<=a[i])	--cnt;
			if(a[j]>a[i])	++cnt;
			L[n+cnt]=i-j;
		}
		cnt=0;
		for(int j=i+1;j<=n;++j){
			if(a[j]<a[i])	--cnt;
			if(a[j]>=a[i])	++cnt;
			R[n+cnt]=j-i;
		}
		for(int j=-n;j<=n;++j)
			if((~L[n+j])&&(~R[n-j]))
				f[i]=max(f[i],L[n+j]+R[n-j]+1);
	}
}

int read_l,read_r;
int main(){
	freopen("beautiful.in","r",stdin);
	freopen("beautiful.out","w",stdout);

	scanf("%d",&n);
	for(int i=1;i<=n;++i)	scanf("%d",a+i);
	init();
	
	for(int i=1;i<=n;++i)	printf("%d ",f[i]);
	puts("\n");
	
	scanf("%d",&Q);
	while(Q--){
		scanf("%d%d",&read_l,&read_r);
		int ans=0;
		for(int i=read_l;i<=read_r;++i)
			ans=max(ans,f[i]);
		printf("%d\n",ans);
	}
	return 0;
}



T3

题意:对于一个可重集合S,有三种操作:add(x) 插入x,del(x)删除x(保证存在x且每次插入删除都只影响一个),cnt(x) 询问对于x,集合中有多少个数y满足x&y==y(值域2^16)

思路:…………考过三次,今天终于会了【躺平】,O(n*√值域),对于每个数的前8位和后8位分开,用cnt[i][j]记录。

           显然,O(1)插入O(n)查询是很简单的,同理,O(n)插入O(1)查询也是很简单的(稍微处理一下就好),于是对于每个数,前8位和后8位分别是修改O(1+2^8)和查询O(1+2^8)就好了,于是就优化成了O(n)的x【某c姓同学表示,2^8是常数23333

#include<bits/stdc++.h>
using namespace std;	int Q;
int cnt[1<<8][1<<8];

char opt[10];
int read_x;
int main(){
	freopen("subset.in","r",stdin);
	freopen("subset.out","w",stdout);
	scanf("%d",&Q);
	while(Q--){
	scanf("%s%d",opt,&read_x);
		if(opt[0]=='a'){
			int tmp=read_x&((1<<8)-1);
			int tmp2=(read_x>>8)&((1<<8)-1);
			for(register int i=0;i<(1<<8);++i){
				if((tmp&i)==tmp)
					++cnt[tmp2][i];
			}
		}
		if(opt[0]=='d'){
			int tmp=read_x&((1<<8)-1);
			int tmp2=(read_x>>8)&((1<<8)-1);
			for(register int i=0;i<(1<<8);++i){
				if((tmp&i)==tmp)
					--cnt[tmp2][i];
			}
		}
		if(opt[0]=='c'){
			int ans=0;
			int tmp=read_x&((1<<8)-1);
			int tmp2=read_x>>8;
			for(register int i=0;i<(1<<8);++i){
				if((tmp2&i)==i)
					ans+=cnt[i][tmp];
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值