模拟测试49

这次考试题还是蛮好的,只是人很水啊

T1

考场思路:

1.每次询问离线出来,再对于每一个k进行操作,$O(klnk)$枚举区间,再利用主席树查前驱,期望时间复杂度$O(nlogn^2)$但可以被卡到$O(n^2logn)$(对于k极小的询问),如果优化就把k极小的询问预处理?不可实现弃辽

2.分块,不会处理每一块的答案,死了

所以正解还是分块,考虑预处理每一块答案。

对于一个k来说,答案是$max_{i=0}^{n/k}max_{j=i*k}^{i*k+k-1} \{a[j]-i*k\}$,开一个桶维护每一块内的元素,扫一边搞出<=每个数的数直接处理即可。复杂度为$O(n^2lnn/S+m*S)$当S取$\sqrt{nlogn}$的时候最小为$O(n\sqrt{nlogn})$

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<iostream>
 4 #define N 100001
 5 #define sN 105
 6 using namespace std;
 7 inline int _max(int a,int b){return a>b?a:b;}
 8 int bl[N+5],ans[sN][N+5],t[N+5],tl[N+5],tot,L[sN],a[N+5];
 9 inline int read()
10 {
11     int x=0,f=1;char c=getchar();
12     while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
13     while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
14     return x*f;
15 }
16 inline void pre()
17 {
18     int now;
19     for(int i=1;i<=tot;i++)
20     {
21         now=L[i];
22         while(bl[now]==i)t[a[now]]++,tl[a[now]]=a[now],now++;
23         for(int j=1;j<=N;j++)if(!t[j])tl[j]=tl[j-1];now=L[i];
24         for(int j=2;j<=N;j++)
25         {
26             for(int k=j-1;k<=N;k+=j)
27                 ans[i][j]=_max(ans[i][j],(tl[k]%j));
28             ans[i][j]=_max(ans[i][j],(tl[N]%j));
29         }
30         while(bl[now]==i)t[a[now]]--,tl[a[now]]=0,now++;
31     }
32 }
33 inline int query(int l,int r,int k)
34 {
35     int mx=0;
36     if(bl[l]==bl[r])
37     {
38         for(int i=l;i<=r;i++)mx=_max(mx,a[i]%k);
39         return mx;
40     }
41     for(int i=l;bl[i]==bl[l];i++)mx=_max(mx,a[i]%k);
42     for(int i=bl[l]+1;i<bl[r];i++)mx=_max(mx,ans[i][k]);
43     for(int i=r;bl[i]==bl[r];i--)mx=_max(mx,a[i]%k);
44     return mx;
45 }
46 int main()
47 {
48     int n=read(),m=read();
49     int t=sqrt(n*20)+1;
50     for(int i=1;i<=n;i++)
51     {
52         bl[i]=(i-1)/t+1;//printf("%d\n",bl[i]);
53         if(bl[i]!=bl[i-1])L[bl[i]]=i;
54         a[i]=read();
55     }tot=bl[n];pre();
56     while(m--)
57     {
58         int l=read(),r=read(),k=read();
59         printf("%d\n",query(l,r,k));
60     }
61     return 0;
62 }
View Code

T2

n^2思路很简单,但是会被卡空间。

所以换个角度,按x排序。

实际上就是找不断往中间收缩的方案数(感性理解)

考虑处理出前i-1个如何弄第i个,设dp[i][0/1]表示以i为起点往左/右走的方案数

如果i是起点,那么给i加上前面比它小的往右撇的方案即可

如果不是,那么i一定是第二个点,枚举起点和第三个点,给起点加上第三个点的贡献。

得出转移方程:

dp[i][0]+=dp[j][1] (j<i&&y[j]<y[i])

dp[j][1]+=dp[z][1] (j<i&&z>j&&z<i&&y[z]<y[j]&&y[j]<y[i])

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define FH_SB 6005
 4 using namespace std;
 5 const int mod=1e9+7;
 6 int dp[FH_SB][2],s[FH_SB];
 7 struct node{
 8     int fh,sb;
 9     inline void init(){scanf("%d%d",&fh,&sb);}
10     friend bool operator <(const node a,const node b){ return a.fh<b.fh;}
11 }a[FH_SB];
12 int main()
13 {
14     int n,ans=0;scanf("%d",&n);
15     for(int i=1;i<=n;i++)a[i].init();
16     sort(a+1,a+n+1);
17     for(int i=1;i<=n;i++)
18     {
19         s[i]=1;dp[i][0]++;dp[i][1]++;
20         for(int j=i-1;j;j--) s[j]=(s[j+1]+dp[j][1]*(a[j].sb<a[i].sb))%mod;
21         for(int j=1;j<i;j++)
22         {
23             if(a[j].sb<a[i].sb)(dp[i][0]+=dp[j][1])%=mod;
24             else (dp[j][1]+=s[j+1])%=mod;
25         }
26     }
27     for(int i=1;i<=n;i++)(ans+=(dp[i][0]+dp[i][1])%mod)%=mod;
28     printf("%d\n",ans-n);
29 }
View Code

 

T3不会,咕了。

 

posted on 2019-09-22 11:36  _kx  阅读(215)  评论(0编辑  收藏  举报