NOI Online #2 提高组
A 涂色游戏
题目描述
你有
1
0
2
0
10^20
1020个格子, 他们从0开始编号, 初始时所有格子都还未染色, 现在你按如下规则对它们进行染色:
1.编号是p1倍数的格子(包括0号格子, 下同)染成红色。
2.编号是p2倍数的格子染成蓝色。
3,编号既是p1倍数也是p2倍数的格子,染成红色或蓝色。
其中p1, p2给定, 若格子编号是p1或p2的倍数则它必须被染色。
在忽略掉所有未染色的格子后, 你不希望存在k个连续的格子颜色相同, 因为你认为在这种染色方案是无聊的。现给定p1, p2, k;你想知道是否有一种染色方案是不无聊的。
题目思路
首先明白这个涂色的格子是一个循环。
我们不妨设有a, b两种颜色, 分别对应p1, p2, 而且p1 > p2;
肯定连续的最长格子是b颜色的, 因为p2小, 颜色密度大, 涂的多。
同时一个循环长度是lcm(p1, p2);然后后面的就和这个循环里的一摸一样。
一个循环里容易得知有p1/gcd(p1, p2)个b格子, p2/gcd(p1, p2)个a格子。
但lcm那里要填a颜色的格子, 因为两边都是b格子。所以b格子数量要-1.
设上面格子数分别为n1(a), n2(b).
则最长连续数就为n1/n2, 或n1/n2+1.(取决于n1和n2是否为倍数)
与k判断即可。
注意:k = 1时, 肯定是No
如果p1 == p2, 则肯定是yes
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <cstdio>
#include <string>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
long long gcd(long long a,long long b)
{return b==0?a:gcd(b,a%b);}
long long T,p1,p2,k,n1,n2,now_gcd,n;
int main()
{
//freopen("color.in","r",stdin);
//freopen("color.out","w",stdout);
scanf("%lld",&T);
while(T--)
{
scanf("%lld %lld %lld",&p1,&p2,&k);
if(k<2)
{
printf("No\n");
continue;
}
if(p1==p2)
{
printf("Yes\n");
continue;
}
if(p1<p2)
p1^=p2^=p1^=p2;
now_gcd=gcd(p1,p2);
n1=p1/now_gcd-1;
n2=p2/now_gcd;
if(n1%n2!=0)
n=(n1/n2)+1;
else
n=n1/n2;
if(n>=k)
printf("No\n");
else
printf("Yes\n");
}
return 0;
}
B 子序列问题
题目描述
给定一个长度为n的正整数序列
A
1
,
A
2
,
⋅
⋅
⋅
⋅
⋅
⋅
⋅
,
A
n
A_1, A_2,·······, A_n
A1,A2,⋅⋅⋅⋅⋅⋅⋅,An.定义一个函数
f
(
l
,
r
)
f(l,r)
f(l,r)表示:序列中下标在
[
l
,
r
]
[l,r]
[l,r]范围内的子区间中, 不同的整数个数, 换句话说,
f
(
l
,
r
)
f(l,r)
f(l,r)就是集合
A
l
,
A
l
+
1
,
⋅
⋅
⋅
⋅
⋅
,
A
r
{A_l, A_{l+1},·····,A_r}
Al,Al+1,⋅⋅⋅⋅⋅,Ar的大小, 这里的集合是不可重集, 即集合中的元素互不相等。
现在, 请你求出
∑
l
=
1
n
∑
r
=
l
n
(
f
(
l
,
r
)
)
2
\sum_{l=1}^n \sum_{r = l}^n (f(l,r))^2
∑l=1n∑r=ln(f(l,r))2。由于答案可能很大, 请对
1
0
9
+
7
10^9+7
109+7取模。
题目思路
暴力怎么做, 两个cigema双重枚举O(n2)。接着, set维护值O(logn), 总时间复杂度O(n2logn)
考虑枚举f的值, 并算他出现了几次, 加到答案里。
但是, 想了好久, 也没有想出来, 因为随机性太大, 找不着一个很明显的特征
于是我们又开始考虑每个位置对答案的贡献, 发现:当位置i这个数在[l,r]里第一次出现时, 才有贡献。
所以我们设一个数组b, b[i]表示a[i]上一次出现的位置。如果是第一次, 则b[i] = 0;
这样以后, 位置i上的数, 贡献的区域为
l
∈
(
b
[
i
]
,
i
]
,
r
∈
[
i
,
n
]
l \in (b[i], i] , r \in [i, n]
l∈(b[i],i],r∈[i,n]这些区间里。
但是, 题目需要求的是平方, 所以我们转化成一个区间中, 任选两个合法位置的方案数, 在反过来求, 枚举两个位置(i,j)算他们能做出贡献的区间数为左端点数量乘上右端点 = (i - max(b[i], b[j])) * (n - j + 1)(i >= b[j])
复杂度O(n^2)
在考虑如何优化,
首先, 如果我们枚举j的话, 那么我们就转而求成了
∑
i
=
1
j
m
a
x
(
p
r
e
[
i
]
,
p
r
e
[
j
]
)
;
\sum_{i = 1} ^ {j} max(pre[i], pre[j]);
∑i=1jmax(pre[i],pre[j]);那么维护最大值其实非常好维护, 两个树状数组就可以达到O(logn)的时间复杂度。
所以, 总的时间复杂度就为O(nlogn)
完结撒花
细节请看代码
//problem:sequence
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int MAXN=1e6,MOD=1e9+7;
inline int mod1(int x){return x<MOD?x:x-MOD;}
inline int mod2(int x){return x<0?x+MOD:x;}
inline void add(int& x,int y){x=mod1(x+y);}
inline void sub(int& x,int y){x=mod2(x-y);}
inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;}
int n,a[MAXN+5],vals[MAXN+5],cnt_v,pre[MAXN+5],pos[MAXN+5],tosub[MAXN+5];
vector<int>vec[MAXN+5];
struct FenwickTree{
int sum[MAXN+5],num[MAXN+5];
void point_add(int p,int ds,int dn){
p++;
for(;p<=n;p+=(p&(-p)))add(sum[p],ds),add(num[p],dn);
}
pii prefix_query(int p){
p++;
int rs=0,rn=0;
for(;p;p-=(p&(-p)))add(rs,sum[p]),add(rn,num[p]);
return mk(rs,rn);
}
FenwickTree(){}
}T;
int main() {
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)cin>>a[i],vals[i]=a[i];
sort(vals+1,vals+n+1);
cnt_v=unique(vals+1,vals+n+1)-(vals+1);
for(int i=1;i<=n;++i){
a[i]=lob(vals+1,vals+cnt_v+1,a[i])-vals;
}
for(int i=1;i<=n;++i){
pre[i]=pos[a[i]];
pos[a[i]]=i;
vec[pre[i]].pb(i);
}
int ans=0;
/*
for(int i=1;i<=n;++i){
for(int j=pre[i]+1;j<=i;++j){
add(ans,(ll)(j-max(pre[i],pre[j]))*(n-i+1)%MOD*(i==j?1:2)%MOD);
}
}
*/
for(int i=1,sumpre=0;i<=n;++i){
if(i>1){
int x=(ll)i*(i-1)/2%MOD;
sub(x,sumpre);
pii q=T.prefix_query(pre[i]);
add(x,q.fi);
sub(x,(ll)q.se*pre[i]%MOD);
x=(ll)x*(n-i+1)*2%MOD;
sub(x,tosub[i]);
add(ans,x);
}
add(ans,(ll)(i-pre[i])*(n-i+1)%MOD);
add(sumpre,pre[i]);
T.point_add(pre[i],pre[i],1);
for(int j=0;j<SZ(vec[i]);++j){
int ii=vec[i][j];
int x=(ll)i*(i+1)/2%MOD;
sub(x,sumpre);
pii q=T.prefix_query(pre[ii]);
add(x,q.fi);
sub(x,(ll)q.se*pre[ii]%MOD);
x=(ll)x*(n-ii+1)*2%MOD;
add(tosub[ii],x);
}
}
cout<<ans<<endl;
return 0;
}
完美。。。