真 头皮发麻
头一次写题写到恶心
怕是以后都不会再去推垃圾数学了
(数论算个屁,打表找规律)
A:
给定一个长度为 n 的序列 a 以及常数 k,序列从 1 开始编号。记
(l, r) = ∑ ai − max {ai}
,求合法的正整数对 (l, r) 的数量,满足 1 ≤ l < r ≤ n,且 k| f (l, r)。
对于 30% 的数据,n ≤ 3000;
对于另外 20% 的数据,数列 a 为随机生成;
对于 100% 的数据,1 ≤ n ≤ 3 × 10^5, 1 ≤ k ≤ 10^6, 1 ≤ ai ≤ 10^9。
看一眼是一个O(n^2)的暴力对吧,然而数据范围只允许你O(nlogn)
所以考虑O(nlogn)的暴力
带了一个log的暴力自然会让你想到二分(在这里称为“分治”更加准确)
在search(l,r)时考虑起点在前半边,终点在后半边的方案数
首先扫一遍后半边,记录下以mid为起点的每个序列可能出现的最大值以及他们的位置
将它与后半边可能出现的最大值进行比较
同时继续更新saveans
(感觉自己在考场上最后3min调出来真是万幸,jry附体)
同时用saveans保存一下最大值在后半边时的情况数
然后扫一遍前半边(从后往前),对于每一次的值,
#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
int a[300500],saveans[1000500][2],//0表示最大值在右边的这一段,1表示最大值在左边这一段
maxpos[300500],maxque[300500],//maxpos:到达当前位置时最大值在哪里,maxque:加入maxpos中出现过的值
n,k,sum[300500];
long long ans=0;
void bisearch(int l,int r)
{
if (l==r) return;
int mid=(l+r)/2,i,cnt=0,maxd=0;
sum[mid]=0;maxque[0]=0;
for (i=mid+1;i<=r;i++)
{
if (a[i]>a[maxque[cnt]]) maxque[++cnt]=i;
sum[i]=(sum[i-1]+a[i]%k)%k;
saveans[(sum[i]-a[maxque[cnt]]%k+k)%k][0]++;maxpos[i]=maxque[cnt];
}
maxd=0;maxque[cnt+1]=r+1;
int s=0,p=1,p1=mid+1;
for (i=mid;i>=l;i--)
{
s=(s+a[i]%k)%k;maxd=max(maxd,a[i]);
while ((p<=cnt) && (maxd>=a[maxque[p]])) p++;
while (p1<maxque[p]) {saveans[(sum[p1]-a[maxpos[p1]]%k+k)%k][0]--;saveans[sum[p1]][1]++;p1++;}
ans+=saveans[(k+maxd%k-s)%k][1];
if (p<=cnt) ans+=saveans[(k-s)%k][0];
}
for (i=mid+1;i<p1;i++) saveans[sum[i]][1]--;
for (i=p1;i<=r;i++) saveans[(sum[i]-a[maxpos[i]]%k+k)%k][0]--;
bisearch(l,mid);
bisearch(mid+1,r);
}
int main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
scanf("%d%d",&n,&k);
int i;
for (i=1;i<=n;i++) scanf("%d",&a[i]);
bisearch(1,n);
printf("%lld",ans);
return 0;
}
B有 m 个在 [0, 2n) 内均匀随机取值的变量,求至少有两个变量取值相同的概率。为了避免精度误差,假设你的答案可以表示成 a 的形式(其中 (a, b) = 1),你需要输出 a 和 b 对 10^6 + 3 取模后的值。
真 辣鸡数学题
正难则反,概率可以被表示为
直接对这个式子求值明显是会猝死的,所以考虑优化
快速幂自不用说
考虑约分时只可能约去2,因此考虑上面含有2 因子数如何求出
注意到一个性质:与含有2的因子数相同(假设(p,q均为正整数且,不是x的约数),
则q是奇数,且
所以原命题得证股原分式的分子含有的2的质因子的个数与(m-1)! 相同
一个很关键的地方被解决了
。。。。。。等等,分子的值怎么求啊
注意到模数很小,只有10^6,且分子也对这个数取模,又注意到分子各式之间相差1,因此当m大于10^6时,分子必有一项是模数的倍数,所以此时分子模出来为0
好像就可以解决了
。。。。。。你说除法啊,直接乘逆元不就好了
#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<map>
using namespace std;
const long long maxd=1e6+3,mind=500002;
long long n,m;
long long pow(long long x,long long y)
{
long long ans=1,now=x;
while (y)
{
long long tmp=y%2;y/=2;
if (tmp) ans=(ans*now)%maxd;
now=(now*now)%maxd;
}
return ans;
}
int main()
{
freopen("random.in","r",stdin);
freopen("random.out","w",stdout);
scanf("%lld %lld",&n,&m);
if (log2(m)>n) {printf("1 1\n");return 0;}
long long tmp=pow(2,n),b=pow(tmp,m),a=1,i;
for (i=0;i<m;i++)
{
a=(a*(tmp-i))%maxd;
if (a==0) break;
}
m--;long long t=n;
for (i=2;i<=m;i<<=1) t+=m/i;
a=(a*pow(mind,t))%maxd;
b=(b*pow(mind,t))%maxd;
printf("%lld %lld",(b-a+maxd)%maxd,b);
return 0;
}
C:对于一个长度为 n,且下标从 1 开始编号的序列 a,我们定义它是「合法的」,当且仅当它满足以下条件:
- a1 = 1
- 对于 i ∈ [1, n),ai ≤ ai+1 ≤ ai + 1 且 ai+1 为正整数;
- 对于任意在 a 中出现过的数 v,记它的出现次数为 s,则 2 ≤ s ≤ 5。
给定一个长度为 n 的序列 a,其中有一些位置为 0,你需要在这些位置上任意填数, 使得 a 成为一个合法的序列,并且最大化 an 的值。
其实C才是最简单的
我们记录两个数组:up与down,up记录当前位置的最大值以及该最大值在序列的长度,down则记录最小值
一个很明显的贪心:up逢二进一,down逢五进一
当前序列若有值,则将这个值直接赋给up与down
最后判断一下如果最后的up与down矛盾就输出-1
输出方案的话从后往前扫一遍就可以了
#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
struct node{
int num,len;
}up[200500],down[200500];
int a[500500],n,t[500500];
int main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
scanf("%d",&n);
int i;
for (i=1;i<=n;i++) scanf("%d",&a[i]);
if (a[1]>1) {printf("-1\n");return 0;}
up[1].num=1;up[1].len=1;down[1].num=1;down[1].len=1;
for (i=2;i<=n;i++)
{
up[i]=up[i-1];down[i]=down[i-1];
up[i].len++;down[i].len++;
if (up[i].len>2) {up[i].len=1;up[i].num++;}
if (down[i].len>5) {down[i].len=1;down[i].num++;}
if (a[i])
{
if (up[i].num>a[i]) {up[i].num=a[i];up[i].len=2;}
else if (up[i].num==a[i]) up[i].len=min(up[i].len,2);
if (down[i].num<a[i]) {down[i].num=a[i];down[i].len=1;}
if ((up[i].num<a[i]) || (down[i].num>a[i])) {printf("-1\n");return 0;}
}
}
if (up[n].len==1) {up[n].num--;up[n].len=up[n-1].len+1;}
if (up[n].num<down[n].num) {printf("-1\n");return 0;}
a[n]=up[n].num;
memset(t,0,sizeof(t));t[a[n]]++;
for (i=n-1;i>=1;i--)
{
if (!a[i])
{
int tmp=min(a[i+1],up[i].num);
if (t[tmp]==5) tmp--;
a[i]=tmp;
}
t[a[i]]++;
}
printf("%d\n",a[n]);
for (i=1;i<=n;i++) printf("%d ",a[i]);
return 0;
}