B.Crazy Binary String
最长子序列直接找
0
0
0和
1
1
1中较少的那个即可
对于最长子串,把
0
0
0看作
−
1
-1
−1的话,即找区间和为
0
0
0的最长区间
数组记录前缀和出现的最早位置就可以了
#include<bits/stdc++.h>
#define N 500000
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int n,i,base,res,x,y;
int pos[N],a[N],s[N];
char ch[N];
int main()
{
scanf("%d",&n);
scanf("%s",ch+1);
fo(i,1,n) if (ch[i] == '0') a[i] = 1; else a[i] = -1;
fo(i,1,n) s[i] = s[i-1] + a[i];
base = n;
fo(i,1,n)
{
if (pos[s[i]+base] == 0 && s[i] != 0) {pos[s[i]+base] = i; continue;}
res = max(res,i-pos[s[i]+base]);
}
cout<<res<<" ";
fo(i,1,n) if (a[i] == 1) x++; else y++;
cout<<min(x,y)*2<<endl;
return 0;
}
D.Big Integer
111...111
=
10
n
−
1
9
≡
0
(
m
o
d
111...111=\frac { { 10 }^{ n }-1 }{ 9 } \equiv 0(mod
111...111=910n−1≡0(mod
p
)
p)
p),等价于
1
0
n
≡
1
(
m
o
d
10^n \equiv 1(mod
10n≡1(mod
9
p
)
9p)
9p)
当
p
=
2
,
5
p=2,5
p=2,5时答案显然为0,否则有
1
0
φ
(
9
p
)
≡
1
(
m
o
d
10^{\varphi(9p)}\equiv1(mod
10φ(9p)≡1(mod
9
p
)
9p)
9p)
我们需要找到
1
0
i
10^i
10i
m
o
d
mod
mod
9
p
9p
9p的循环节d,那么有
d
d
d
∣
|
∣
φ
(
9
p
)
\varphi(9p)
φ(9p),暴力枚举
φ
(
9
p
)
\varphi(9p)
φ(9p)的约数即可,之后问题转化为求
d
d
d
∣
|
∣
i
j
i^j
ij的组数
(ps:这里做快速幂有可能爆long long,当然std用了另一种做法,即特判掉
p
p
p和
9
9
9不互质的情况,然后就没有这个
9
9
9了)
这是一个经典的问题,设
d
=
p
1
k
1
p
2
k
2
.
.
.
p
l
k
l
d=p_{1}^{k_1}p_{2}^{k_2}...p_{l}^{k_l}
d=p1k1p2k2...plkl
对于一个
j
j
j,设
g
=
p
1
⌈
k
1
j
⌉
p
2
⌈
k
2
j
⌉
.
.
.
p
l
⌈
k
l
j
⌉
g=p_{1}^{\lceil \frac{k_1}{j} \rceil}p_{2}^{\lceil \frac{k_2}{j} \rceil}...p_{l}^{\lceil \frac{k_l}{j} \rceil}
g=p1⌈jk1⌉p2⌈jk2⌉...pl⌈jkl⌉,则
i
i
i必须是
g
g
g的倍数
然后可以发现
j
>
30
j > 30
j>30之后
g
g
g不会变化(因为
k
i
≤
30
k_i \le 30
ki≤30),直接乘起来即可
#include<iostream>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define N 100
using namespace std;
long long T,n,m,i,j,k;
long long d,p,g,res;
long long prime[N],exp1[N],expp[N];
long long qm(long long a,long long x,long long mod)
{
__int128_t res = 1; __int128_t e = a;
//long long res = 1; long long e = a;
while (x)
{
if (x&1) res = (res * e) % mod;
e = (e * e) % mod;
x = x / 2;
}
return res;
}
bool check(long long x)
{
if (qm(10,x,9*p) == 1)
return true;
else return false;
}
int main()
{
scanf("%lld",&T);
while (T--)
{
res = 0;
scanf("%lld%lld%lld",&p,&n,&m);
if (p == 2 || p == 5) {printf("0\n"); continue;}
long long pp = 6 * (p - 1);
d = pp;
fo(i,2,sqrt(pp))
if ((pp) % i == 0)
{
if (check(i)) d = min(d,i);
if (check(pp/i)) d = min(d,pp/i);
}
//cout<<d<<endl<<endl;
k = 0;
fo(i,2,sqrt(d))
if (d % i == 0)
{
k++; prime[k] = i; exp1[k] = 0;
while (d % i == 0)
{
exp1[k]++; d = d / i;
}
}
if (d > 1) {k++; prime[k] = d; exp1[k] = 1;}
fo(j,1,min(1ll*30,m))
{
fo(i,1,k)
{
expp[i] = exp1[i] / j;
if (exp1[i] % j > 0) expp[i]++;
}
g = 1;
fo(i,1,k)
if (g <= n)
while (expp[i]--)
{
g = g * prime[i];
if (g > n) break;
}
else break;
res += n / g;
}
if (m > 30) res += 1ll * (n / g) * (m - 30);
printf("%lld\n",res);
}
return 0;
}
G.Removing Stones
显然,最多的一堆不能超过总和的一半
那么我们可以每次找到区间的最大值的位置
k
k
k,左右两个区间中,枚举落在较小的区间的端点的位置,另外一个端点可以二分得到(因为区间越长,区间和越大,越有可能是答案)
然后分成两半继续递归
注意这里必须较小区间枚举、较大区间二分,否则有可能退化为
N
2
N^2
N2
第一次用线段树找区间最大值T了,后来改成ST表才过掉
当然这里还有许多小优化,比如二分区间单调变小,找不到了可以退出等等。。。。
但是用线段树是一定不行的,因为线段树的复杂度是跑满的(甚至应该是大于)
O
(
N
l
o
g
2
N
)
O(Nlog^2N)
O(Nlog2N)
#include<iostream>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define N 300005
using namespace std;
int mx[N*4];
int st[N][20];
int n,i,T;
long long res;
long long sum[N],a[N];
void build(int rt,int l,int r)
{
if (l == r) {mx[rt] = a[l]; return;}
int mid = (l + r) >> 1;
build(rt<<1,l,mid); build(rt<<1|1,mid+1,r);
mx[rt] = max(mx[rt<<1],mx[rt<<1|1]);
}
int query(int rt,int l,int r,int L,int R)
{
if (l == r) return l;
int mid = (l + r) >> 1;
int x=-1,y=-1;
if (L <= mid) x = query(rt<<1,l,mid,L,R);
if (mid+1 <= R) y = query(rt<<1|1,mid+1,r,L,R);
if (x == -1) return y; else if (y == -1) return x; else
if (a[x] >= a[y]) return x; else return y;
}
int query2(int l,int r)
{
int len = r - l + 1;
int p = log(len)/log(2);
int x = st[l][p];
int y = st[r-(1<<p)+1][p];
if (a[x] < a[y]) return y; else return x;
}
void dfs(int l,int r)
{
if (r -l + 1 < 2) return;
int k,ll,rr,L,R,p;
k = query2(l,r);
if (k <= ((l + r) >> 1))
{
L = k; R = r; p = -1;
fo(ll,l,k)
{
//if (p == -1) {L = k; R = r;} else {L = p; R = r; p = -1;}
L = k; R = r; p = -1;
while (L <= R)
{
int mid = (L + R) >> 1;
if ((sum[mid] - sum[ll-1]) / 2 >= a[k]) {p = mid; R = mid - 1;}
else L = mid + 1;
}
if (p != -1) res += r - p + 1;// else break;
}
} else
{
L = l; R = k; p = -1;
fd(rr,r,k)
{
//if (p == -1) {L = l; R = k;} else {L = l; R = p; p = -1;}
L = l; R = k; p = -1;
while (L <= R)
{
int mid = (L + R) >> 1;
if ((sum[rr] - sum[mid-1]) / 2 >= a[k]) {p = mid; L = mid + 1;}
else R = mid - 1;
}
if (p != -1) res += p - l + 1;// else break;
}
}
dfs(l,k-1); dfs(k+1,r);
}
void ST()
{
int i,j,x,y,len;
fo(i,1,n) st[i][0] = i;
fo(j,1,log(n)/log(2))
{
len = 1 << j;
fo(i,1,n-len+1)
{
x = st[i][j-1]; y = st[i+len/2][j-1];
if (a[x] < a[y]) st[i][j] = y; else st[i][j] = x;
}
}
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]);
ST();
fo(i,1,n) sum[i] = sum[i-1] + a[i];
//build(1,1,n);
res = 0;
dfs(1,n);
cout<<res<<endl;
}
return 0;
}
H.Magic Line
让你找一条直线平分平面上的
n
n
n个点
按照
x
x
x升序,再按照
y
y
y降序排序,找到第
n
/
2
n/2
n/2个点
以这个点作竖直线,然后往顺时针转一点点,就可以把左上和右下分开
#include <bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define N 1005
using namespace std;
struct www{int x,y;} f[N];
int T,n,i,mid;
bool cmp(const www &a,const www &b) {if (a.x != b.x) return a.x < b.x; else return a.y > b.y;}
void print(int a,int c,int b,int d)
{
int q = 900000000;
int x1,y1,x2,y2;
if ((a+b)%2 == 0) {x1 = (a+b)/2+1; x2 = (a+b)/2-1;} else {x1 = (a+b)/2+1; x2 = (a+b)/2;}
if ((c+d)%2 == 0) {y1 = (c+d)/2+1+q; y2 = (c+d)/2-1-q;} else {y1=(c+d)/2+1+q; y2 = (c+d)/2-q;}
printf("%d %d %d %d\n",x1-N,y1-N,x2-N,y2-N);
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
fo(i,1,n) scanf("%d%d",&f[i].x,&f[i].y);
fo(i,1,n) {f[i].x+=N; f[i].y+=N;}
sort(f+1,f+n+1,cmp);
mid = n / 2;
print(f[mid].x,f[mid].y,f[mid+1].x,f[mid+1].y);
}
}