2017 3-5 周考 解题报告

~~又是一周一度的周考~~

T1:permut

1.1 题目描述
求由 1 到 n 一共 n 个数字组成的所有排列中,逆序对个数为 k 的有多少个
1.2 输入格式
第一行为一个整数 T,为数据组数。
以下 T 行,每行两个整数 n, k,意义如题目所述。
1.3 输出格式
对每组数据输出答案对 10000 取模后的结果
1.4 Sample Input
1
4 1
1.5 Sample Output
3
1.6
数据范围及约定
对于 30% 的数据,满足 n ≤ 12。
对于所有数据,满足 n ≤ 1000, k ≤ 1000, T ≤ 10。

解题思路:

 第一眼咋一看,逆序对?归并!!然而并不是。

 接着仔细一读,排列中的逆序对……排序是有顺序的,想想排列中的交换某些位置后逆序对数的变化,of course是有规律的。

设f[i][j]为由1到i组成的序列中,逆序对数最多为j的有多少。

当多加一个数后,逆序对最多增加i,最少为0。

得到转移方程:

if(j<i) f[i][j]=(f[i][j-1]+f[i-1][j]);

else f[i][j]=(f[i][j-1]+f[i-1][j]-f[i-1][j-i]);

最后输出为f[n][k]-f[n][k-1].

 

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e3+2,mod=1e4;
int f[maxn][maxn]={0};
int main()
{
	freopen("permut.in","r",stdin);
	freopen("permut.out","w",stdout);
	int t=0,n,k;
	scanf("%d",&t);
	f[0][0]=1;
	for(int i=1;i<=1001;i++)
	{
		f[i][0]=f[0][i]=1;
	}
	for(int i=1;i<=1001;i++)
	{
		for(int j=1;j<=1001;j++)
		{
			if(j<i) f[i][j]=(f[i][j-1]+f[i-1][j])%mod;
			else f[i][j]=(f[i][j-1]+mod+f[i-1][j]-f[i-1][j-i])%mod;//mod时记得先加mod,还有i不要写成1了,虽然经常int i=1,然而i!=1;
		}
	}
	for(int i=1;i<=t;i++)
	{
		scanf("%d%d",&n,&k);
		cout<<(f[n][k]+mod-f[n][k-1])%mod<<endl;
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T2: beautiful

2.1题目描述
一个长度为 n 的序列,对于每个位置 i 的数 ai 都有一个优美值,其定义是:找到序列中最长的一段 [l, r],满足 l ≤ i ≤ r,且 [l, r] 中位数为 ai(我们比较序列中两个位置的数的大小时,以数值为第一关键字,下标为第二关键字比较。这样的话 [l, r] 的长度只有可能是奇数), r - l+ 1 就是 i 的优美值。接下来有 Q 个询问,每个询问 [l, r] 表示查询区间 [l, r] 内优美值的最大值。
2.2输入格式
第一行输入 n 接下来 n 个整数,代表 ai 接下来 Q,代表有 Q 个区间接下来 Q 行,每行
两个整数 l, r(l ≤ r),表示区间的左右端点
2.3 输出格式
对于每个区间的询问,输出答案
2.4 Sample Input
8
16 19 7 8 9 11 20 16
8
3 8
1 4
2 3
1 1
5 5
1 2
2 8
7 8
2.5 Sample Output
7 3 1 3 5 3 7 3
3
2.6数据范围及约定
对于 30% 的数据,满足 n, Q ≤ 50
对于 70% 的数据,满足 n, Q ≤ 2000
对于所有数据,满足 n ≤ 2000, Q ≤ 100000,ai ≤ 200

解题思路:

仔细看看这道题,中位数?——》有序奇数互异序列左右数字个数相等,优美值?——》符合题意的区间大小。

理解题意就简单了,对于每个数,分别向左右扫描一遍,遇到大于的就++,else --,

然后分别记录下来,再对于这些状态求max,找出每个点的优美值。

         最后将l->r扫一遍,ans=max,得出答案。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b))
#define clsm(a) memset(a,255,sizeof(a))
using namespace std;
const int maxn=2010;
int l[maxn*2],r[maxn*2],a[maxn],w[maxn];
int main()
{
	int n,q;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=n;i++)
	{
		clsm(l),clsm(r);
		l[n]=r[n]=0;
		int cnt=0;
		for(int j=i-1;j>=1;j--)
		{
			if(a[i]<a[j]) cnt++;
			else cnt--;
			l[n+cnt]=i-j;
		}
		cnt=0;
		for(int j=i+1;j<=n;j++)
		{
			if(a[i]<=a[j]) cnt++;
			else cnt--;
			r[n+cnt]=j-i;
		}
		for(int j=1-i;j<=i-1;j++)
		{
			if(l[n+j]>=0&&r[n-j]>=0)
			{
				w[i]=max(w[i],l[n+j]+r[n-j]+1);
			}
		}
	}
	scanf("%d",&q);
	while(q--)
	{
		int L,R;
		scanf("%d%d",&L,&R);
		int ans=0;
		for(int i=L;i<=R;i++)
		{
			ans=max(ans,w[i]);
		}
		cout<<ans<<endl;
	}
	return 0;
}

 

T3:subset

3.1题目描述
一开始你有一个空集,集合可以出现重复元素,然后有 Q 个操作
1. add s
在集合中加入数字 s。
2. del s
在集合中删除数字 s。保证 s 存在
3. cnt s
查询满足 a&s = a 条件的 a 的个数
3.2 输入格式
第一行一个整数 Q 接下来 Q 行,每一行都是 3 个操作中的一个
3.3 输出格式
对于每个 cnt 操作输出答案
3.4 Sample Input
7
add 11
cnt 15
add 4
add 0
cnt 6
del 4
cnt 15
3.5 Sample Output
1 2 2
3.6 数据范围及约定
对于 30% 的数据满足: 1 ≤ n ≤ 1000
对于 100% 的数据满足, 1 ≤ n ≤ 200000 , 0 < s < 216

解题思路:

先开始并没有像出来,216用set绝对会炸,看了看题解,听了听lifel讲,大概知道了题解之意。

分块计算。 a[pre][suf],其中 pre < 28, suf < 28,表示前面 8 位是 pre,后面 8 位是 suf 的子集的数字的个数。
那么对于每个 add 和 del 操作都可以最多 28 时间枚举 suf 更新 a 数组。
对于 cnt 操作,最多 28 枚举 pre,计算答案即可。

Code

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=301;
int a[maxn][maxn];
int main()
{
	int q=0,x=0;
	char str[5];
	scanf("%d",&q);
	for(int t=1;t<=q;t++)
	{
		int w=1;
		scanf("%s%d",str,&x);
		if(str[0]=='d') w=-1;
		if(str[0]!='c')
		{
			int pre=x>>8,cur=x&255,cp=cur^255;
			a[pre][cur]+=w;
			for(int i=cp;i;i=(i-1)&cp)
			{
				a[pre][i|cur]+=w;
			}
		}
		else
		{
			int pre=x>>8,cur=x&255;
			int ans=a[0][cur];
			for(int i=pre;i;i=(i-1)&pre)
			{
				ans+=a[i][cur];
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}

 

posted @ 2017-03-06 21:09  SpaceSkyNet  阅读(591)  评论(0编辑  收藏  举报