noip模拟测试12
老样子,还是先写一下自己的考试经过,首先看题面,觉得开题顺序1 2 3,同时,注意到T2为一个类似于退式子的题,吸取v上次的教训,给他留出足够时间。
首先是 T1,我又想到了线段树,但是没有想到正解,但是我想到一个 n^n/2 的分组算法,但是比较麻烦,没有分出来,就打了个暴力,一共用时一小时左右
第二题,一看就是个跟平时数学文化课作业难度差不多的一个概率题,但是我当时看错了 n,m 的输入顺序,导致我的结果和样例一个都对不上,用了差不多一个小时才发现自己把 m,n 看反了
发现之后就很好说了,五分钟左右就推出了式子,但是这个式子要求取模并约分,我当时就怀疑直接取模计算的正确性,但是没什么好的想法,就直接打了
T3,我当时想到了贪心,但是我的贪心思路没有正确性,正解的贪心思路真是妙,接下来我对每道题进行详细解析:
T1:这道题思路来自: https://www.cnblogs.com/hzoi-fengwu
这道题类似与昨天的T3,同样的,我们可以利用单调队列求出每个 a[i] ,当作最大值的左右区间,区间没有交集,并利用启发式合并的思想,暴力枚举小的区间,理智处理较大区间,
这样就可以省去枚举左右端点。利用入阵曲的思路,若 a ,b,在 %p 意义下同余,(设 a>b ) 则 (a-b) 可以被 p 整除。
那么我们就可以利用一个vector数组,第一维存模,第二维存元素的位置,以上就是预处理的过程
接下来就是一个非常秒的计算,我们以左区间小于右区间为例,我们从 [L,p],枚举每一个sum[i-1],因为满足条件的右端点 x 必定满足 ,sum[x]==((sum[i-1]+a[p])%k),
我们设 X=((sum[i-1]+a[p])%k),那么我们通过计算Y=lower_bound(v[X].begin(),v[X].end(),p)-v[X].begin(),和Z=upper_bound(v[X].begin(),v[X].end(),R)-v[X].begin();做差即可求出满足条件的右端点个数
最后 ans+=Z-Y即可
注意,当左区间大于右区间时,因为我们要查询 Y=lower_bound(v[X].begin(),v[X].end(),L-1)-v[X].begin(),当 L==1时,会查到0,所以我们对于 v[0].push_back(0),意思就是前0位的前缀和为 0,%p后余数也为零
代码如下:
1 #include<bits/stdc++.h>
2 #define int long long
3 #define re register int
4 #define iv inline void
5 #define ii inline int
6 #define lc rt<<1
7 #define rc rt<<1|1
8 #define mid ((l+r)>>1)
9 using namespace std;
10 const int N=5e6+10;
11 int n,k;
12 int a[N],l[N],r[N],sum[N],ans;
13 stack <pair<int,int> > s;
14 vector <int> v[N];
15 ii read()
16 {
17 int x=0,f=1;
18 char ch=getchar();
19 while(ch<'0'||ch>'9')
20 {
21 if(ch=='-')
22 f=0;
23 ch=getchar();
24 }
25 while(ch>='0'&&ch<='9')
26 {
27 x=(x<<1)+(x<<3)+(ch^48);
28 ch=getchar();
29 }
30 return (f)?x:(-x);
31 }
32 void in()
33 {
34 for(re i=1;i<=n;i++)
35 {
36 while(!s.empty()&&a[i]>=s.top().first)
37 {
38 r[s.top().second]=i-1;
39 s.pop();
40 }
41 s.push(make_pair(a[i],i));
42 }
43 int L=s.top().second;
44 while(!s.empty())
45 {
46 r[s.top().second]=L;
47 s.pop();
48 }
49 for(re i=n;i;i--)
50 {
51 while(!s.empty()&&a[i]>s.top().first)
52 {
53 l[s.top().second]=i+1;
54 s.pop();
55 }
56 s.push(make_pair(a[i],i));
57 }
58 int R=s.top().second;
59 while(!s.empty())
60 {
61 l[s.top().second]=R;
62 s.pop();
63 }
64 }
65 iv workk(int p,int L,int R)
66 {
67 if(p-L<R-p)
68 {
69 int X,Y,Z;
70 for(re i=L;i<=p;i++)
71 {
72 X=(sum[i-1]+a[p])%k;
73 Y=lower_bound(v[X].begin(),v[X].end(),p)-v[X].begin();
74 Z=upper_bound(v[X].begin(),v[X].end(),R)-v[X].begin();
75 ans+=max(0*1ll,Z-Y);
76 }
77 }
78 else
79 {
80 int X,Y,Z;
81 for(re i=p;i<=R;i++)
82 {
83 X=(sum[i]-a[p]%k+k)%k;
84 Y=lower_bound(v[X].begin(),v[X].end(),L-1)-v[X].begin();
85 Z=upper_bound(v[X].begin(),v[X].end(),p-1)-v[X].begin();
86 ans+=max(0ll,Z-Y);
87 }
88 }
89 }
90 #undef int
91 int main()
92 {
93 #define int long long
94 n=read();
95 k=read();
96 for(re i=1;i<=n;i++)
97 a[i]=read();
98 in();
99 for(re i=0;i<k;i++)
100 v[i].push_back(0);
101 for(re i=1;i<=n;i++)
102 sum[i]=(sum[i-1]+a[i])%k;
103 for(re i=1;i<=n;i++)
104 v[sum[i]%k].push_back(i);
105 for(re i=1;i<=n;i++)
106 workk(i,l[i],r[i]);
107 ans-=n;
108 printf("%lld\n",ans-());
109 return 0;
110 }
T2
这道题式子很好推,就是 ( ( C^m,(2^n) ) *m! )/2^(n*m), == ( ((2^n)!) / (((2^n)-m)!) )/(2^(n*m));
上面的部分就是 (2^n) *((2^n) -1) *((2^n)-2)* ..... *((2^n)-m+1) , 也就是m 个连续的数相乘
那么显然,当 m>mo 时,分子就是 0
接下来我们考虑约分,这道题麻烦的地方就在于不能用 gcd ,因为模了一个数后结果会不对
有这样一个结论,
那么分子就转化成求 (m-1)! 中的2的因子数
可以这样求:
for(re i=2;i<m;i<<=1) ans+=(m-1)/i;
最后,我们利用逆元的性质求出答案即可
代码如下:
#include<bits/stdc++.h>
#define int long long
#define re register int
#define iv inline void
#define ii inline int
#define lc rt<<1
#define rc rt<<1|1
#define mid ((l+r)>>1)
using namespace std;
int m,n,sum,U,D;
int mo=1e6+3;
inline long long read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return (f)?x:(-x);
}
ii ksm(int d,int z)
{
int out=1;
while(z)
{
if(z&1)
out=out*d%mo;
z>>=1;
d=d*d%mo;
}
return out%mo;
}
#undef int
int main()
{
#define int long long
n=read();
m=read();
if(log2(m)>n)
{
printf("1 1\n");
return 0;
}
U=1,D=1;
int base=ksm(2,n),ans=0,inv;
D=ksm(base,m-1);
for(re i=2;i<m;i<<=1)
ans+=(m-1)/i;
inv=ksm(ksm(2,ans),mo-2);
if(m<=mo)
for(re i=1;i<m;i++)
U=U*(base-i)%mo;
else
U=0;
U=(U%mo*inv%mo+mo)%mo;
D=(D%mo*inv%mo+mo)%mo;
printf("%lld %lld\n",((D-U+mo)%mo+mo)%mo,(D+mo)%mo);
return 0;
}
T3
一道思路非常妙的贪心题
那么我们就开两个二元组,up,down,按照上述思路操作即可
具体来说,我们先正着扫一遍,先不考虑是否当前位置有数,继承up 和 down
up两位一进,down五位一进
考虑已经填的,先考虑上界,若a[i]>up,比上界大肯定不合法,若a[i]=up,不管,若a[i]<up,则将up调整到a[i]次数变为2,下界类似,若a[i]<down,比下界小不合法,若a[i]>down,将down调整到a[i],统计答案时反着扫
最后输出的时候记得右端点特判
代码如下:
1 #include<bits/stdc++.h>
2 #define int long long
3 #define re register int
4 #define iv inline void
5 #define ii inline int
6 #define lc rt<<1
7 #define rc rt<<1|1
8 #define mid ((l+r)>>1)
9 using namespace std;
10 const int N=2e5+10;
11 int n,ans=-1,maxn;
12 int a[N],vis[N],v[N],cun[N];
13 pair<int,int> up[N],down[N];
14 ii read()
15 {
16 int x=0,f=1;
17 char ch=getchar();
18 while(ch<'0'||ch>'9')
19 {
20 if(ch=='-')
21 f=0;
22 ch=getchar();
23 }
24 while(ch>='0'&&ch<='9')
25 {
26 x=(x<<1)+(x<<3)+(ch^48);
27 ch=getchar();
28 }
29 return (f)?x:(-x);
30 }
31 #undef int
32 int main()
33 {
34 #define int long long
35 n=read();
36 for(re i=1;i<=n;i++)
37 a[i]=read(),vis[a[i]]++;
38 up[1].first=up[1].second=down[1].first=down[1].second=1;
39 bool noo=0;
40 for(re i=2;i<=n;i++)
41 {
42 if(up[i-1].second==2)
43 {
44 up[i].first=up[i-1].first+1;
45 up[i].second=1;
46 }
47 else
48 {
49 up[i].first=up[i-1].first;
50 up[i].second=up[i-1].second+1;
51 }
52 if(down[i-1].second==5)
53 {
54 down[i].first=down[i-1].first+1;
55 down[i].second=1;
56 }
57 else
58 {
59 down[i].first=down[i-1].first;
60 down[i].second=down[i-1].second+1;
61 }
62 if(a[i])
63 {
64 if(a[i]>up[i].first)
65 {
66 noo=1;
67 break;
68 }
69 if(a[i]<down[i].first)
70 {
71 noo=1;
72 break;
73 }
74 if(a[i]<up[i].first)
75 {
76 up[i].first=a[i];
77 up[i].second=2;
78 }
79 if(a[i]>down[i].first)
80 {
81 down[i].first=a[i];
82 down[i].second=1;
83 }
84 }
85 }
86 if(noo)
87 {
88 printf("-1\n");
89 return 0;
90 }
91 if(a[n])
92 cun[n]=a[n];
93 else
94 {
95 cun[n]=up[n-1].first;
96 ++vis[cun[n]];
97 }
98 for(re i=n-1;i;i--)
99 {
100 if(a[i])
101 cun[i]=a[i];
102 else
103 {
104 int tmp=min(up[i].first,cun[i+1]);
105 while(vis[tmp]==5)
106 --tmp;
107 cun[i]=tmp;
108 ++vis[cun[i]];
109 }
110 }
111 printf("%lld\n",cun[n]);
112 for(re i=1;i<=n;i++)
113 printf("%lld ",cun[i]);
114 return 0;
115 }