2017-3-5 周考解题报告
1. 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
1.7 思路
刚开始拿到题目时,内心OS:woc 这绝对是全卷最难的一道题了(做毕克的题做出心理阴影了)。不过仔细读题之后发现这道题其实是个简单的 DP 逻辑递推题。
假设 f[i,k] 是 1...i 的全排列中逆序对为k的情况个数。相应的排列可以从部分 i-1 的排列中插入 i 转换而来,因为 i 是绝对大于 1...(i-1) 的任意一个数的。因此插入 i 这个行为所添加的逆序对个数直接与插入位置相关。这样的话转移方程就很好出来了。
f[i,j]=f[i,j-1]+f[i-1,j] (j≤i)
f[i,j]=f[i,j-1]+f[i-1,j]-f[i-1,j-i] (j>i)
(然而我的代码里并不是完全按这方程来的)
1.8 代码
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 #include <sstream> 7 #include <algorithm> 8 #include <cmath> 9 #include <stack> 10 #include <queue> 11 #include <list> 12 13 using namespace std; 14 15 const int mod=10000; 16 int T; 17 int n[15],k[15]; 18 int f[1100][1100]; 19 int ls[1100]; 20 int mn,mk; 21 22 int GetSum(int x,int y){ 23 if(x-y<0) return ls[x]%mod; 24 return (ls[x]-ls[x-y]+2*mod)%mod; 25 } 26 27 int main(){ 28 freopen("permut.in","r",stdin); 29 freopen("permut.out","w",stdout); 30 scanf("%d",&T); 31 for(int i=0;i<T;i++){ 32 scanf("%d%d",&n[i],&k[i]); 33 mn=max(mn,n[i]); mk=max(mk,k[i]); 34 } 35 for(int i=1;i<=mn;i++){ 36 if(i==1) f[i][0]=1; 37 else for(int j=0;j<=mk;j++) 38 f[i][j]=GetSum(j,i); 39 for(int j=0;j<=mk;j++){ 40 if(j==0) ls[j]=f[i][j]%mod; 41 else ls[j]=(f[i][j]+ls[j-1])%mod; 42 } 43 } 44 for(int i=0;i<T;i++) printf("%d\n",f[n[i]][k[i]]); 45 fclose(stdin); 46 fclose(stdout); 47 return 0; 48 }
2. 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
2.6 数据范围与约定
对于 30% 的数据,满足 n,Q≤50
对于 70% 的数据,满足 n,Q≤2000
对于所有数据,满足n≤2000, Q≤100000,ai≤200
2.7 思路
做题时想也没想就去写了Splay(还写RE了!),后来看了题解才发现直接n2扫一遍+计数就可以了。然后就是裸的RMQ了!
RMQ部分随便写就是了,想用 ST 表的用 ST 表,想用线段树的就用线段树,反正我用的是直接记录 max 值。
2.8 代码
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 #include <sstream> 7 #include <algorithm> 8 #include <cmath> 9 #include <stack> 10 #include <queue> 11 #include <list> 12 #include <set> 13 14 using namespace std; 15 16 int n,seq[2200]; 17 int a[2200],Q; 18 int l[2200*2],r[2200*2]; 19 20 int main(){ 21 freopen("beautiful.in","r",stdin); 22 freopen("beautiful.out","w",stdout); 23 scanf("%d",&n); 24 for(int i=1;i<=n;i++){ 25 scanf("%d",&seq[i]); 26 } 27 for(int i=1;i<=n;i++){ 28 memset(l,255,sizeof(l)); l[n]=0; 29 memset(r,255,sizeof(r)); r[n]=0; 30 int cnt=0; 31 for(int j=i+1;j<=n;j++){ 32 if(seq[j]>=seq[i]) cnt++; 33 if(seq[j]<seq[i]) cnt--; 34 r[n+cnt]=j-i; 35 } 36 cnt=0; 37 for(int j=i-1;j>=1;j--){ 38 if(seq[j]>seq[i]) cnt++; 39 if(seq[j]<=seq[i]) cnt--; 40 l[n+cnt]=i-j; 41 } 42 for(int j=1-i;j<=i-1;j++) 43 if(l[n+j]>=0&&r[n-j]>=0) 44 a[i]=max(a[i],l[n+j]+1+r[n-j]); 45 } 46 scanf("%d",&Q); 47 while(Q--){ 48 int l,r; 49 scanf("%d%d",&l,&r); 50 int ans=0; 51 for(int i=l;i<=r;i++) ans=max(ans,a[i]); 52 printf("%d\n",ans); 53 } 54 fclose(stdin); 55 fclose(stdout); 56 return 0; 57 }
3. subset
作者太懒了,就放个链接吧: