2019牛客多校第三场

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=910n10(mod p ) p) p),等价于 1 0 n ≡ 1 ( m o d 10^n \equiv 1(mod 10n1(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=p1jk1p2jk2...pljkl,则 i i i必须是 g g g的倍数
然后可以发现 j &gt; 30 j &gt; 30 j>30之后 g g g不会变化(因为 k i ≤ 30 k_i \le 30 ki30),直接乘起来即可

#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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值