• 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
    ",ans);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    MySQL的删除语句
    机器学习-K-means聚类及算法实现(基于R语言)
    机器学习-线性回归补充-实践
    什么是JWT
    Java的NIO
    音乐播放器歌词同步显示
    Linux和Mac下安装RocketMQ过程
    支付宝沙箱应用
    HTML5 调用手机摄像头拍照
    JavaWeb图片上传的几种方式
  • 原文地址:https://www.cnblogs.com/spaceskynet/p/6509479.html
Copyright © 2020-2023  润新知