1.计数 (count.cpp/c/pas)
时间限制:
1s
内存限制:
256MB
【问题描述】
给出
m
个数
求
1−n
中有多少数不是
a[1],a[2],⋯,a[m]
的倍数。
【输入】
输入文件名为count.in。
第一行,包含两个整数:
n,m
第二行,包含
m
个数,表示
【输出】
输出文件名为count.out。
输出一行,包含
1
个整数,表示答案
【输入输出样例】
count.in | count.out |
---|---|
10 2 2 3 | 3 |
【数据说明】
对于60%的数据,
1≤n≤106
对于另外20%的数据,
m=2
对于100%的数据,
1≤n≤109,0≤m≤20,1≤a[i]≤109
solution
一眼秒掉的题,就是一个小学生容斥。
还有就是一个小优化,如果 m 个数中存在倍数关系,那就取最小的那个
比如说有两个数,
2 和 4 ,如果一个数是4 的倍数,那他也肯定是 2 的倍数,所以只求2 的就好了。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
template<typename T>
void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
#define MAXM 25
ll a[MAXM];
ll gcd(ll a,ll b) {
return b==0?a:gcd(b,a%b);
}
ll lcm(ll a,ll b) {
return a/gcd(a,b)*b;
}
bool cmp(ll a,ll b) {
return a>b;
}
int main() {
int n,m;
input(n),input(m);
for(int i=0;i<m;i++)
input(a[i]);
sort(a,a+m);
if(a[1]==1) {
printf("0");
return 0;
}
for(int i=0;i<m;i++) {
if(a[i]==0) continue;
for(int j=i+1;j<m;j++)
if(a[j]%a[i]==0)
a[j]=0;
}
sort(a,a+m,cmp);
for(int i=0;i<m;i++)
if(a[i]==0) {
m=i;
break;
}
//上面就是我说的那个优化,下面是容斥,可能用二进制写有点恶心,不过短小精悍
int ans=n,sum;
ll x;
for(int s=1;s<1<<m;s++) {
sum=0,x=1;
for(int i=0;i<m;i++)
if(s&(1<<i)) {
x=lcm(x,a[i]);
sum++;
}
if(x>n) continue;
if(sum&1) ans-=n/x;
else ans+=n/x;
}
printf("%d",ans);
return 0;
}
2.第k大区间(kth.cpp/c/pas)
时间限制:
1s
内存限制:
256MB
【问题描述】
定义一个长度为奇数的区间的值为其所包含的的元素的中位数。
现给出
n
个数,求将所有长度为奇数的区间的值排序后,第
【输入】
输入文件名为kth.in。
第一行两个数
n
和
第二行,
n
个数。(
【输出】
输出文件名为kth.out。
一个数表示答案。
【输入输出样例】
kth.in | kth.out |
---|---|
4 3 3 1 2 4 | 2 |
【样例解释】
[l,r]
表示区间
l−r
的值
[1,1]:3
[2,2]:1
[3,3]:2
[4,4]:4
[1,3]:2
[2,4]:2
【数据说明】
对于
30%
的数据,
1≤n≤100
对于
60%
的数据,
1≤n≤300
对于
80%
的数据,
1≤n≤1000
对于
100%
的数据,
1≤n≤100000,k≤
奇数区间的数
solution
求第 k 大这种题一般是二分答案,所以这道题目也是二分
我们二分中位数
mid ,然后问题就转换成了怎样快速求出有多少个长度为奇数的区间中位数 ≥mid这里利用了一个特别巧妙的做法
二分之后我们把每个数都重新赋值, ≥mid 的数设为 1 ,
<mid 的数设为 0重新赋值之后,如果一个区间的中位数是
mid ,那这个区间所有数的和应该是 r−l+12+1我们令 sum 为重新赋值之后的前缀和,那二分之后求的也就是满足 sum[r]−sum[l−1]≥r−l+12+1 的区间个数
整数的离散性:如果 a,b∈Z ,那么 a≥b 可以表示为 a>b−1
根据上面那个东西,我们可以把式子变成 sum[r]−sum[l−1]>r−l+12
然后再把分母乘过去,移项就可以得到 2∗sum[l−1]−(l−1)<2∗sum[r]−r
发现这个式子的两边形式差不多,所以设数组 c ,
c[i]=2∗sum[i]−i 然后式子就变成了 c[l−1]<c[r] ,也就是要求满足 c[l−1]<c[r] 的区间的个数
又因为 l−1<r ,所以最后求的也就是 c 数组的顺序对个数
最后需要说明的
因为长度为奇数的区间的左右端点
l,r 奇偶性是相同的,所以 l−1,r 的奇偶性是不同的,所以我们需要分奇偶统计答案求顺序对我用的是树状数组,会求逆序对的应该可以看懂…
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
template<typename T>
void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
#define MAXN 100010
ll k;
int n,m;
int a[MAXN],b[MAXN];
int t[MAXN];
inline int lowbit(int x) {
return x&(-x);
}
void modify(int x) {
for(;x<=m;x+=lowbit(x))
t[x]++;
return;
}
int query(int x) {
int ans=0;
for(;x;x-=lowbit(x))
ans+=t[x];
return ans;
}
int c[MAXN],d[MAXN];
int Lower_bound(int x) {
int l=1,r=m,mid;
while(l<r) {
mid=l+r>>1;
if(x<=d[mid]) r=mid;
else l=mid+1;
}
return l;
}
ll Judge(int mid) {
c[0]=0;
for(int i=1;i<=n;i++)
c[i]=c[i-1]+(a[i]>=mid);
for(int i=0;i<=n;i++)
c[i]=c[i]*2-i,d[i+1]=c[i];
sort(d+1,d+n+2);
int rank=1;
for(int i=2;i<=n+1;i++)
if(d[i]!=d[rank])
d[++rank]=d[i];
m=rank;
ll ans=0;
for(int i=0;i<=n;i++)
c[i]=Lower_bound(c[i]);
memset(t,0,sizeof(t));
for(int i=0;i<=n;i++)
if(i&1) modify(c[i]);
else ans+=(ll)query(c[i]-1);
memset(t,0,sizeof(t));
for(int i=0;i<=n;i++)
if((i&1)==0) modify(c[i]);
else ans+=(ll)query(c[i]-1);
return ans;
}
int main() {
input(n),input(k);
for(int i=1;i<=n;i++)
input(a[i]),b[i]=a[i];
sort(b+1,b+n+1);
int rank=1;
for(int i=2;i<=n;i++)
if(b[i]!=b[rank])
b[++rank]=b[i];
int l=1,r=rank,mid;
ll result;
while(l<r) {
mid=(l+r>>1)+1;
result=Judge(b[mid]);
if(result==k) {
printf("%d\n",b[mid]);
return 0;
} else if(result>k) l=mid;
else r=mid-1;
}
printf("%d\n",b[l]);
return 0;
}
3.区间求和(sum.cpp/c/pas)
时间限制:
2s
内存限制:
256MB
【问题描述】
有
n
个数,给定一个
【输入】
输入文件名为sum.in。
输入五个数
n,a1,A,B,C
。
a1
表示第一个数,
A,B,C
用来生成其余
n−1
个数。
a[i]=(a[i−1]∗A+B)modC
1≤n≤1000000,0≤a1,A,B,C≤1000000000
【输出】
输出文件名为sum.out。
一个数表示答案,最后答案对
1000000007
取模。
【输入输出样例】
sum.in | sum.out |
---|---|
3 3 1 1 10 | 63 |
【样例解释】
三个数为
3,4,5
K=1:[1,1]=3,[1,2]=[2,2]=4,[1,3]=[2,3]=[3,3]=5
(表示各个区间在
k=1
时的答案)
K=2:[1,2]=7,[2,3]=[1,3]=9
K=3:[1,3]=12
【数据说明】
对于
30%
的数据,
1≤n≤100
对于
60%
的数据,
1≤n≤300
对于
80%
的数据,
1≤n≤1000
对于
100%
的数据,
1≤n≤1000000
solution
假设我们已经知道了区间 [l,r] 的答案是 ans ,考虑新加入一个数 a[r+1] 对答案会造成什么影响
设 c 为
[l,r] 中 ≤a[r+1] 的数的个数设 sum 为区间 [l,r] 中 >a[r+1] 的数的和
那么区间 [l,r+1] 的答案就是 ans+(c+1)∗a[r+1]+sum
令 f[i] 表示以 i 为右端点的所有的区间的答案之和
即
f[i]=∑l=1ians[l,i] 把上面推的式子代进去,可以得到
f[i]=f[i−1]+i∗a[i]+∑a[j]≥a[i]j∗a[j]+∑a[k]<a[i]k∗a[i]最后 ans=∑i=1nf[i]
然后上面那个东西可以用数据结构来维护,这里我选择树状数组
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
template<typename T>
void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
#define mod 1000000007
struct Fenwick_Tree {
static const int MAXN=1000010;
ll tree[MAXN];
int n;
Fenwick_Tree() {
memset(tree,0,sizeof(tree));
}
int lowbit(int x) {
return x&(-x);
}
void Modify(int k,ll x) {
for(;k<=n;k+=lowbit(k))
tree[k]+=x,tree[k]%=mod;
return;
}
int query(int k) {
int ans=0;
for(;k;k-=lowbit(k))
ans+=tree[k],ans%=mod;
return ans%mod;
}
int query(int l,int r) {
return (query(r)-query(l-1)+mod)%mod;
}
};
Fenwick_Tree t[2];
#define MAXN Fenwick_Tree::MAXN
ll a[MAXN],b[MAXN];
int Rank;
int f[MAXN];
int Binary_Chop(ll x) {
int l=1,r=Rank,mid;
while(l<r) {
mid=l+r>>1;
if(x<=b[mid]) r=mid;
else l=mid+1;
}
return l;
}
int main() {
ll n,A,B,C;
input(n),input(a[1]),
input(A),input(B),input(C);
A%=C,B%=C;
a[1]%=C;
for(int i=2;i<=n;i++)
b[i]=a[i]=(a[i-1]*A+B)%C;
b[1]=a[1];
sort(b+1,b+n+1);
Rank=1;
for(int i=2;i<=n;i++)
if(b[i]!=b[Rank])
b[++Rank]=b[i];
f[0]=0;
t[0].n=t[1].n=n;
ll sum;
for(int i=1,pos;i<=n;i++) {
pos=Binary_Chop(a[i]);
sum=(ll)i*a[i]%mod;
sum+=(ll)t[0].query(pos)*a[i]%mod;
sum+=(ll)t[1].query(pos+1,n)%mod;
f[i]=(ll)(f[i-1]+sum)%mod;
t[0].Modify(pos,i);
t[1].Modify(pos,i*a[i]);
}
int ans=0;
for(int i=1;i<=n;i++)
ans=(ll)(ans+f[i])%mod;
printf("%d\n",ans%mod);
return 0;
}