题目:(开始自己描述题目了...)
第一题大意:
求1~n的所有排列中逆序对为k个的方案数,输出方案数%10000,n<=1000。
解:这道题一个递推,因为我基本上没怎么自己做过递推,所以推了一个小时,而其实熟练后几分钟十多分钟就推出来了。好吧,我递推的方法:从n=1 开始递推,当n=2的时候由 n=1 推出,以此类推。如何递推?以n=3,k=3为例:有三种方式结尾,以3结尾,前两个数由1,2 排列,3在1,2后面不产生逆序对,那么方案数就等于当n=2的时候产生3个逆序对的方案数,为0 ;以2结尾,2在1,3后面产生1个逆序对,那么方案数就等于当n=2的时候产生2个逆序对的方案数0;以1 结尾,1在2,3后面已产生2个逆序对,那么方案数就等于当n=2的时候产生1个逆序对的方案数1.那么总方案为三种情况的和:1。同样的道理,我们可以得到所有n、k 的情况。。。就自己再推吧,画图比较好理解。。。我就不说了。。
注意:在取mo的时候,如果前面的式子有 减号,那应该再 +mo 再% mo.... 为此我被坑了70分输出负数。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<map> 5 #define maxn 1005 6 #define inf 10000 7 #define ll long long 8 using namespace std; 9 int t,an[maxn],ak[maxn],man,mak; 10 int last_k; 11 ll m[maxn][maxn],sum[maxn][maxn]; 12 int main() 13 { 14 freopen("permut.in","r",stdin); 15 freopen("permut.out","w",stdout); 16 cin>>t; 17 for (int i=1;i<=t;i++) 18 { 19 scanf("%d%d",&an[i],&ak[i]); 20 if (an[i]>man) man=an[i]; 21 if (ak[i]>mak) mak=ak[i]; 22 } 23 last_k=0; 24 for (int i=1;i<=man;i++) 25 m[i][0]=sum[i][0]=1; 26 for (int i=2;i<=man;i++) 27 { 28 int j; 29 for (j=1;j<=last_k+i-1;j++) 30 { 31 if (j>mak) break; 32 if (j<=i-1){ 33 if(j<=last_k){ 34 m[i][j]=(m[i-1][j]+m[i][j-1])%inf; 35 } 36 else m[i][j]=(sum[i-1][last_k])%inf; 37 } 38 else 39 { 40 if(j<=last_k){ 41 m[i][j]=((sum[i-1][j]-sum[i-1][j-i])%inf+inf)%inf;//否则输出负数 !! 42 } 43 else m[i][j]=((sum[i-1][last_k]-sum[i-1][j-i])%inf+inf)%inf;//否则输出负数 44 45 } 46 47 sum[i][j]=(sum[i][j-1]+m[i][j])%inf; 48 // printf("%I64d ",m[i][j]%inf); 49 } 50 //cout<<endl; 51 last_k=j-1; 52 } 53 for (int i=1;i<=t;i++) 54 printf("%I64d ",m[an[i]][ak[i]]%inf); 55 return 0; 56 }
第二题大意:
有一个数字序列,其中每一个 ai 都有对应的优美值(- -),即找一个包含这个ai的最长的区间,并且ai为这个区间的中位数,那么优美值即这个区间的长度(一定是奇数)。询问对于一个[l,r]的区间中最大的优美值。
解:1、找优美值。一个数组s。在ai的左右两边,aj如果大于ai,s[j]=1,否则为-1,然后扫描一遍序列,找到最长的区间使s值加起来为0,那么在这个区间内ai肯定是中位数。
2、查询。可以用线段树,或者RMQ。RMQ更简单一点,就是一个链上倍增。线段树写起来太复杂,下一次来复习。
3、注意当aj与ai相等时,如果aj在ai左边,则s[j]是-1,右边是+1,所以要注意>和>= 的区别。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define maxn 2005 6 using namespace std; 7 int n,q,num[maxn],be[maxn],f[maxn][16],ma[maxn][16]; 8 int s[maxn],sum[maxn],posl[2*maxn],posr[2*maxn]; 9 void get_beautiful() 10 { 11 for (int i=1;i<=n;i++) 12 { 13 s[i]=sum[i]=0; 14 memset(posl,0,sizeof (posl)); 15 memset(posr,0,sizeof (posr)); 16 posl[n]=posr[n]=i; 17 for (int j=i-1;j>=1;j--)//倒序 18 { 19 if (num[j]>num[i]) s[j]=1;//> 字典序 20 else s[j]=-1; 21 sum[j]=sum[j+1]+s[j]; 22 posl[n+sum[j]]=j;//+ 23 } 24 for (int j=i+1;j<=n;j++) 25 { 26 if (num[j]>=num[i]) s[j]=1;// 27 else s[j]=-1; 28 sum[j]=sum[j-1]+s[j]; 29 posr[n+sum[j]]=j; 30 } 31 for (int j=0;j<=2*n;j++) 32 if (posl[2*n-j]&&posr[j])//** posr[2*n-j] ** 33 be[i]=max(be[i],posr[j]-posl[2*n-j]+1);//** posr[2*n-j] ** 34 } 35 } 36 void RMQ() 37 { 38 for (int i=1;i<=14;i++) 39 for (int j=1;j<=n;j++) 40 { 41 f[j][i]=f[f[j][i-1]][i-1]; 42 ma[j][i]=max(ma[j][i-1],ma[f[j][i-1]][i-1]); 43 } 44 } 45 void query(int l,int r) 46 { 47 int ans=0,d=r-l,now=l; 48 if (l==r)//******** 49 { 50 printf("%d ",be[l]); 51 return ; 52 } 53 for (int i=0;i<=14;i++) 54 { 55 if ((1<<i)&d){ 56 ans=max(ma[now][i],ans); 57 now=f[now][i]; 58 } 59 } 60 printf("%d ",ans); 61 } 62 int main() 63 { 64 freopen("beautiful.in","r",stdin); 65 freopen("beautiful.out","w",stdout); 66 cin>>n; 67 for (int i=1;i<=n;i++) 68 scanf("%d",&num[i]); 69 get_beautiful(); 70 for (int i=1;i<n;i++) 71 { 72 f[i][0]=i+1; 73 ma[i][0]=max(be[i],be[i+1]); 74 } 75 RMQ(); 76 cin>>q; 77 for (int i=1;i<=q;i++) 78 { 79 int l,r; 80 scanf("%d%d",&l,&r); 81 query(l,r); 82 } 83 return 0; 84 }
第三题大意:
有三个操作:add 一个数字 delete一个数字,cnt 一个数字s:找数组中ai & s = ai的个数并输出。s<2^16
解:
把s分为两块,2^8和2^8。首先要明白:当s的一位上为1 的时候,如果满足ai & s = ai,那么ai那一位上可以为1,0。所以可以说ai是s的子集,s是ai的父集。
add s的时候用s找它的负集,cnt s的时候找s的子集的个数。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<algorithm> 6 #define maxn 260 7 using namespace std; 8 int q,a[maxn][maxn]; 9 int main() 10 { 11 freopen("subset.in","r",stdin); 12 freopen("subset.out","w",stdout); 13 cin>>q; 14 for (int i=1;i<=q;i++) 15 { 16 char s[5]; 17 int x,w=1; 18 scanf("%s%d",s,&x); 19 if (s[0]=='d') w=-1; 20 if (s[0]!='c'){ 21 int pre=(x>>8),suf=(x&255),comp=255^suf;//找到s中所有 0,改为 1 22 a[pre][suf]+=w;//**** 23 for (int j=comp;j!=0;j=(j-1)&comp)//父集:各种情况的 0 改为 1 24 a[pre][(j|suf)]+=w; 25 } 26 else{ 27 int pre=(x>>8),suf=(x&255); 28 int ans=a[0][suf]; 29 for (int j=pre;j!=0;j=(j-1)&pre)//子集:各种情况的 1 改为 0 30 ans+=a[j][suf]; 31 printf("%d ",ans); 32 } 33 } 34 return 0; 35 }