赞
踩
Alex有n个限制条件,分以下三类:
Alex想知道有多少个数k可以满足条件,题目保证答案是有限个的(即至少包括一个限制1和一个限制2)。
第一行一个整数
t
t
t,表示
t
t
t 组数据
(
1
≤
t
≤
500
)
(1 \leq t \leq 500)
(1≤t≤500)
每组数据的第一行有一个整数
n
n
n,表示
n
n
n 个限制条件
(
2
≤
n
≤
100
)
(2 \leq n \leq 100)
(2≤n≤100)
接下来
n
n
n 行每行包括两个正整数
a
a
a 和
x
x
x ,
a
=
=
1
a==1
a==1 时表示限制1,
a
=
=
2
a==2
a==2 时表示限制2,
a
=
=
3
a==3
a==3 时表示限制3,x的含义在题意中
(
a
∈
{
1
,
2
,
3
}
,
1
≤
x
≤
1
0
9
)
(a \in \{1,2,3\}, \, 1 \leq x \leq 10^9)
(a∈{1,2,3},1≤x≤109)
每组数据输出一个整数,表示满足条件k的个数。
如果有多个限制1,那么只有x最大的那个限制1能生效,其他的都是多余的,同理限制2。再把限制1和限制2围出的区间中的限制3去掉,剩下的就是有效的k的个数。
所以用一个变量
l
l
l 存储限制1最大的x,同理
r
r
r 存储限制2最小的x。把所有限制3都存下来,最后排个序,然后在上面二分查找
l
l
l 和
r
r
r ,下标之差就是区间
[
l
,
r
]
[l,r]
[l,r] 中限制3的个数。
注意
l
l
l 可能大于
r
r
r ,这时候没有满足条件的k,答案为0。
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int maxn=105; int T,n; int l,r,a[maxn],cnt; int main(){ cin>>T; while(T--){ cin>>n; cnt=l=0;r=2e9; for(int i=1,op,t;i<=n;i++){ cin>>op>>t; if(op==1)l=max(l,t); else if(op==2)r=min(r,t); else a[++cnt]=t; } sort(a+1,a+cnt+1); int t=r-l+1-(upper_bound(a+1,a+cnt+1,r)-lower_bound(a+1,a+cnt+1,l)); cout<<max(0,t)<<endl; } return 0; }
Alice和Bob玩一个游戏,他们有一个数组 a 1 , a 2 , … , a n a_1, a_2,\ldots,a_n a1,a2,…,an ,这个游戏有两个步骤:
Alice想要让最后的数组元素之和最大,Bob想要元素之和最小,如果两人都发挥最佳状态,问最后元素的和是什么
第一行一个整数
t
t
t,表示
t
t
t 组数据
(
1
≤
t
≤
1
0
4
)
(1 \leq t \leq 10^4)
(1≤t≤104)
每组数据第一行有三个整数
n
,
k
,
x
n,k,x
n,k,x,表示
n
n
n 表示数组a的大小,
k
,
x
k,x
k,x 含义同题意。
(
1
≤
n
≤
2
⋅
1
0
5
)
(1 \leq n \leq 2 \cdot 10^5)
(1≤n≤2⋅105)
接下来一行
n
n
n 个整数
a
1
,
a
2
,
…
,
a
n
a_1, a_2,\ldots, a_n
a1,a2,…,an
(
1
≤
a
i
≤
1000
)
(1 \leq a_i \leq 1000)
(1≤ai≤1000),表示数组元素。
保证所有数据
n
n
n 的总和不超过
2
⋅
1
0
5
2 \cdot 10^5
2⋅105
每组数据输出一个整数,表示最后元素之和。
从Bob的视角来看,Alice移除后,要想让数组最后的和最小,一定会给剩余的元素中前x个最大的数乘上-1。那么Alice要删掉一些大数(也就是变成0),防止它变成负的大数,Alice不会删Bob不选的数。
假如数组a排序后是有序的降序排列,数组总和为 t o t tot tot。Bob把一个元素 a i a_i ai ( 1 ≤ i ≤ x ) (1\leq i \leq x) (1≤i≤x)变为 − a i -a_i −ai,那么 t o t tot tot 就损失了 2 ∗ a i 2*a_i 2∗ai(先失去一个 a i a_i ai,又要减去一个 a i a_i ai)。而Alice删掉一个元素 a i a_i ai, t o t tot tot 只损失了 a i a_i ai。但是因为Alice删掉了一个Bob选的数,所以Bob会新选择第 x + 1 x+1 x+1 大的元素,Bob造成的损失变成了 ∑ k = 1 x 2 ∗ a k + 2 ∗ a i − 2 ∗ a x + 1 \sum_{k=1}^{x}{2*a_k}+2*a_i-2*a_{x+1} ∑k=1x2∗ak+2∗ai−2∗ax+1。删掉 a i a_i ai后数组剩余元素之和比删掉之前变化了 a i − 2 ∗ a x + 1 a_i-2*a_{x+1} ai−2∗ax+1 ,因为选 i i i 不影响 x + 1 x+1 x+1 ,所以我们肯定选最小的 i i i,这时候 a i a_i ai 最大。
所以先计算出Bob一开始会造成的损失,然后Alice尝试删掉前 i i i 个最大的元素, O ( k ) O(k) O(k)递推Bob造成的损失和Alice造成的损失,可以算出所有可能的情况,因为AIice先手 拥有选择权,取最大值即可。
时间复杂度 O ( n ∗ l o g n + x + k ) O(n*logn+x+k) O(n∗logn+x+k)。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int maxn=4e5+5; typedef long long ll; int T,n,k,x; int a[maxn]; int main(){ cin>>T; while(T--){ cin>>n>>k>>x; ll tot=0; memset(a,0,sizeof(a)); for(int i=1;i<=n;i++) cin>>a[i],tot+=a[i]; sort(a+1,a+n+1,greater<int>()); ll maxx,tmp=0,pre=0; for(int i=1;i<=x;i++) tmp+=-2*a[i]; maxx=tot+tmp; for(int i=1;i<=k;i++){ pre+=-a[i]; tmp+=2*a[i]-2*a[i+x]; maxx=max(maxx,tot+pre+tmp); } cout<<maxx<<endl; } return 0; }
给你一个n和数组a
你可以把数组a分成k个等长的连续子数列(k是n的因数),如下:
[
a
1
,
a
2
,
…
,
a
k
]
,
[
a
k
+
1
,
a
k
+
2
,
…
,
a
2
k
]
,
…
,
[
a
n
−
k
+
1
,
a
n
−
k
+
2
,
…
,
a
n
]
[a_1,a_2,\ldots,a_k],[a_{k+1}, a_{k+2},\ldots,a_{2k}],\ldots,[a_{n-k+1},a_{n-k+2},\ldots,a_{n}]
[a1,a2,…,ak],[ak+1,ak+2,…,a2k],…,[an−k+1,an−k+2,…,an]
如果存在一个数
m
m
m
(
m
≥
2
)
(m \geq 2)
(m≥2) ,把所有数都模
m
m
m,使得这
k
k
k 个连续子数列完全一致,答案数+1
问总的答案数
每个测试由多个测试用例组成。第一行包含一个整数 t t t ( 1 ≤ t ≤ 1 0 4 ) (1 \leq t \leq 10^4) (1≤t≤104) —— 测试用例的数量。测试用例的说明如下
每个测试用例的第一行包含一个整数 n n n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) (1 \leq n \leq 2\cdot10^5) (1≤n≤2⋅105) —— 数组的长度
每个测试用例的第二行包含 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2,\ldots, a_n a1,a2,…,an ( 1 ≤ a i ≤ n ) (1 \leq a_i \leq n) (1≤ai≤n) —— 数组的元素
可以保证n在所有测试用例中不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2⋅105
每组用例输出一个整数——答案数
假设分成 k k k 段,每段长为 l = n / k l=n/k l=n/k,这个 k k k 可以 O ( n ) O( \sqrt n) O(n ) 暴力枚举。如果一个 m m m 满足条件,那么 k k k 段每个对应位置上(也就是 a i , a i + l , … a i + l ∗ j a_i,a_{i+l},\dots a_{i+l*j} ai,ai+l,…ai+l∗j)模出来的数都一样,即模 m m m 同余,两两之差是 m m m 的倍数。
由于这个两数之差为 m m m 的倍数的性质是传递的(即若 a 1 a_1 a1 与 a 2 a_2 a2 之差和 a 2 a_2 a2 与 a 3 a_3 a3 之差为 m m m 的倍数,那么 a 1 a_1 a1 与 a 3 a_3 a3 之差也是 m m m 的倍数),所以 a i , a i + l , … a i + l ∗ j a_i,a_{i+l},\dots a_{i+l*j} ai,ai+l,…ai+l∗j 里取所有相邻两数的差再求它们的 g c d gcd gcd ,得到的就是满足 a i , a i + l , … a i + l ∗ j a_i,a_{i+l},\dots a_{i+l*j} ai,ai+l,…ai+l∗j同余的最大模数(也就是这一个对应位置上可以满足的最大的m)。
把每个对应位置上的最大模数都算出来,然后求它们的 g c d gcd gcd,得到的就是最大的 m m m,如果它大于等于2,答案数就+1。
暴力枚举 k k k 是 O ( n ) O( \sqrt n) O(n ) 的,验证 k k k 符合条件需要枚举每个对应位置一共 l = n / k l=n/k l=n/k 个,每个对应位置循环 k k k 次,一共循环 n n n 次,算上 g c d gcd gcd 的 l o g n logn logn。最坏时间复杂度是 O ( n n l o g n ) O(n\sqrt n logn) O(nn logn),时限有3秒之多,可以通过。
#include <iostream> #include <cstdio> using namespace std; const int maxn=2e5+5; int T,n,a[maxn]; inline int gcd(int a,int b){//gcd(a,0)=a while(b)b^=a^=b^=a%=b; return a; } bool check(int l){ int tmp=0; for(int i=1;i<=l;i++) for(int j=i+l;j<=n;j+=l) tmp=gcd(tmp,abs(a[j]-a[j-l])); //srds,这里不一定非得是相邻的,因为关系是传递的 //所以减谁都没关系(当然不能减自己),重要的是可以推出所有关系 return tmp!=1; } int main(){ cin>>T; while(T--){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; int cnt=0; for(int i=1;i*i<=n;i++){ if(n%i==0){ if(check(i))cnt++; if(i*i!=n && check(n/i))cnt++; } } cout<<cnt<<endl; } return 0; }
Jayden 有一个数组a,最初是空的。有n个操作,他必须按给定顺序执行两种类型的操作:
Jayden有q次查询。对于每个查询,您需要告诉他a数组的第 k 个元素是什么,数组的元素编号从1开始。
每个测试由多个测试用例组成。第一行包含一个整数 t ( 1 ≤ t ≤ 5000 ) (1 \leq t \leq 5000) (1≤t≤5000) —— 测试用例的数量。测试用例的说明如下:
每个测试用例的第一行包含两个整数n和q ( 1 ≤ n , q ≤ 1 0 5 ) (1 \leq n, q \leq 10^5) (1≤n,q≤105) —— 操作数和查询数。
以下n行描述操作。每行包含两个整数b和x
(
b
∈
{
1
,
2
}
)
(b \in \{1, 2\})
(b∈{1,2}),其中b表示操作的类型。如果 b=1
则 x
(
1
≤
x
≤
n
)
(1 \leq x \leq n)
(1≤x≤n) 是 Jayden 附加到数组末尾的整数。如果 b=2 则 x
(
1
≤
x
≤
1
0
9
)
(1 \leq x \leq 10^9)
(1≤x≤109) 是 Jayden 附加到数组末尾的副本数。
每个测试用例的下一行包含q个整数 k 1 , k 2 , … , k q k_1, k_2, \ldots, k_q k1,k2,…,kq ( 1 ≤ k i ≤ min ( 1 0 18 , c ) ) (1 \leq k_i \leq \min(10^{18}, c)) (1≤ki≤min(1018,c)),表示查询,其中c是完成所有操作后数组的大小。
保证在所有测试用例中n的和 与 q的和都不超过 1 0 5 10^5 105。
对每组数据的每个询问 k i k_i ki ,输出一个整数,表示第 k i k_i ki 个元素是什么
操作1好说,新增了一个什么数直接就是操作数x。操作2把前面复制x遍放在后面,后面的某个位置上数可以算出原来在前面的什么位置上,这个数要么是操作2复制过来的,要么是操作1新增的。如果正好原来的这个位置上的数是操作1新增的,就直接返回这个操作数,如果是操作2复制过来的,就像上面一样继续去算 执行操作2前 原来在什么位置上。
所以给你一个 k k k,我们需要找到它是通过什么操作被新增出来的。很容易想到,如果被第 i i i 个操作新增出来,那么前 i − 1 i-1 i−1 个操作得到的数列元素个数 < k \lt k <k,前 i i i 个操作得到的数列元素个数 ≥ k \ge k ≥k。因此读入操作的时候记录一下第 i i i 个操作是什么,并计算通过前 i i i 个操作得到了多少元素,之后查询的时候二分查找一下 k k k 即可。因为我们还需要知道操作1新增的是什么数,所以再记录一下第 i i i 个操作的操作数即可。
因为操作2至少要给原数组长度乘2,最多60次就会超出 1 0 18 10^{18} 1018,所以一次查询最多找60次,算上二分查找,总的时间复杂度是 O ( 60 ∗ q ∗ l o g n + n ) O(60*q*logn+n) O(60∗q∗logn+n)
#include <iostream> #include <cstdio> using namespace std; typedef long long ll; const int maxn=1e5+5; const ll inf=1e18; int T,n,q; ll a[maxn];//一共几个数 int opt[maxn],num[maxn];//操作 操作数 ll query(ll x){ for(int i=lower_bound(a+1,a+n+1,x)-a;i>=1;i=lower_bound(a+1,a+i+1,x)-a){ if(opt[i]==1)return num[i]; x=((x-1)%a[i-1])+1;//进行操作2前 原来的位置 } } int main(){ cin>>T; while(T--){ cin>>n>>q; for(int i=1,op,t;i<=n;i++){ cin>>op>>t; if(op==1){ a[i]=a[i-1]+1; num[i]=t; opt[i]=1; } else { if((inf+t)/(t+1)<a[i-1])a[i]=inf+1;//除法防爆longlong else a[i]=a[i-1]*(t+1); num[i]=t+1;//增加t个副本,再加上本身相当于长度乘t+1 opt[i]=2; } } ll t; for(int i=1;i<=q;i++){ cin>>t; cout<<query(t)<<endl; } } return 0; } /* 1 10 3 1 1 1 2 1 9 2 111111110 2 6 2 142857142 2 2 1 8 1 7 1 6 333333333333333333 999999999999999999 1000000000000000000 9 9 8 */
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。