校内练习1:Central Europe Regional Contest 2019 解题报告

A . ABB

题意:给出字符串S,求在S后面最少加多少个字符串使得其为回文串。 n ≤ 2 e 5 n \le 2e5 n2e5
解题思路:相当于找一个最小的i使得S[i+1, n]为回文串,直接枚举i然后用字符串hash判断回文串即可。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
using namespace std;
#define ull unsigned long long
ull sed = 1e9 + 93;
const int maxn = 4e5 + 50;
char s[maxn];
ull ha[maxn], ha2[maxn], p[maxn];
ull get_ha1(int l, int r){
    
    
    return ha[r] - ha[l-1]*p[r-l+1];
}
ull get_ha2(int l, int r){
    
    
    return ha2[l] - ha2[r+1]*p[r-l+1];
}
bool check(int l, int r){
    
    
    return get_ha1(l, r) == get_ha2(l, r);
}
int main()
{
    
    
    int n; scanf("%d", &n);
    scanf("%s", s+1);
    p[0] = 1;
    fors(i,1,n+1) ha[i] = ha[i-1]*sed + s[i]-'a', p[i] = p[i-1]*sed;
    for(int i = n; i > 0; --i) {
    
    
        ha2[i] = ha2[i+1]*sed + s[i]-'a';
    }
    int ans = 0;
    for(int i = 1; i <= n; ++i){
    
    
        if(check(i,n)) break;
        ans++;
    }
    cout<<ans<<endl;
}

B. Be Geeks!

前置知识:单调栈、st表、二分
题意:求所有区间的 区间gcd*区间max 的和。 n ≤ 2 e 5 n \le 2e5 n2e5
解题思路:对于这种答案中包含 区间max 的贡献的题目,一般会从这个max入手。有两种方式:分治或者枚举每个点作为max可以控制的区间。实际上两种方式都可以解决本题,这里选择枚举每个点作为max的方式。
利用单调栈找出每个点左边第一个大于等于它的位置 L [ i ] L[i] L[i],和右边第一个大于它的位置 R [ i ] R[i] R[i],这样就得到了它作为max控制的区间 [ L [ i ] + 1 , R [ i ] − 1 ] [L[i]+1, R[i]-1] [L[i]+1,R[i]1]。我们知道, g c d ( a [ x ] , a [ x + 1 ] . . . , i ) gcd(a[x],a[x+1]..., i) gcd(a[x],a[x+1]...,i) L [ i ] + 1 ≤ x ≤ i L[i]+1\le x \le i L[i]+1xi,最多有log个取值:每次变化一次,gcd的值至少减半。所以我们分别处理出 ( L [ i ] , i ] 和 [ i , R [ i ] ) (L[i], i]和[i, R[i]) (L[i],i][i,R[i])的gcd段,这个处理可以使用二分实现。获取区间gcd可以用st表优化到nlogv(如果用线段树就多个log).
对于处理出来的两边的gcd,log^2的进行枚举,获取每对gcd对答案的贡献。这样对于每一个作为max的a[i],复杂度最差是 n l o g 3 n nlog^3n nlog3n,并且很难跑满,常数很小。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
using namespace std;
const int maxn = 2e5 + 50;
int n;
int a[maxn];
int g[maxn][20];
int lg2[maxn];
int qry(int l, int r){
    
    
    int k = lg2[r-l+1];
    return __gcd(g[l][k], g[r-(1<<k)+1][k]);
}
int L[maxn], R[maxn];
stack<int> s;
#define P pair<int,int>
vector<P> u, v;
const int mod = 1000000007;
int last[maxn];
int main()
{
    
    
    lg2[0] = -1;
    fors(i,1,maxn) lg2[i] = lg2[i>>1]+1;
    scanf("%d", &n);
    fors(i,1,n+1) scanf("%d", &a[i]), g[i][0] = a[i];
    for(int j = 1;(1<<j) <= n; ++j){
    
    
        for(int i = 1; i + (1<<(j-1)) <= n; ++i){
    
    
            g[i][j] = __gcd(g[i][j-1], g[i+(1<<(j-1))][j-1]);
        }
    }
    //cout<<g[1][2]<<" "<<g[1][2]<<endl;
    fors(i,1,n+1){
    
    
        while(s.size() && a[s.top()] < a[i]) s.pop();
        if(s.size()) L[i] = s.top();
        else L[i] = 0;
        s.push(i);
    }
    while(s.size()) s.pop();

    for(int i = n; i > 0; --i){
    
    
        while(s.size() && a[s.top()] <= a[i]) s.pop();
        if(s.size()) R[i] = s.top();
        else R[i] = n+1;
        s.push(i);
    }
    ll res = 0;
    fors(i,1,n+1){
    
    
        //cout<<"L:"<<L[i]<<" R:"<<R[i]<<endl;
        int t = qry(L[i]+1, i);
        u.clear();
        int cur = a[i], p = i;
       // cout<<"t:"<<t<<endl;
        while(cur != t){
    
    
            int l = L[i]+1, r = p;
            int ans;
            while(l <= r){
    
    
                if(qry(mid, i) == cur){
    
    
                    ans = mid; r = mid-1;
                }else l = mid+1;
            }
            u.pb(P(cur, ans));
            cur = qry(ans-1, i);
            p = ans-1;
            //cout<<"cur:"<<cur<<endl;
        }
        u.pb(P(cur, L[i]+1));

        t = qry(i, R[i]-1);
        v.clear();
        cur = a[i], p = i;
        while(cur != t){
    
    
            int l = p, r = R[i]-1;
            int ans;
            while(l <= r){
    
    
                if(qry(i,mid) == cur){
    
    
                    ans = mid; l = mid+1;
                }else r = mid-1;
            }
            v.pb(P(cur, ans));
            cur = qry(i, ans+1);
            p = ans+1;
        }
        v.pb(P(cur, R[i]-1));
//        fors(i,0,u.size()){
    
    
//            cout<<"val:"<<u[i].first<<" pos:"<<u[i].second<<endl;
//        }cout<<endl;
        int p1 = i;
        fors(j,0,u.size()){
    
    
            int g1 = u[j].first;
            int l = u[j].second;
            int p2 = i;
            int num1 = p1-l+1;
            fors(k,0,v.size()){
    
    
                int g2 = v[k].first;
                int r = v[k].second;
                int num2 = (r-p2+1);
                ll cnt = (ll)num1*(num2)%mod;
                res = (res + cnt*a[i]%mod * (__gcd(g1,g2))%mod )%mod;
                p2 = r+1;
                //cout<<"i:"<<i<<" l:"<<l<<" r:"<<r<<" g:"<<__gcd(g1,g2)<<endl;
            }
            p1 = l-1;
        }
    }
    last[1] = 1;
    for(int i = 2; i <= n; ++i){
    
    
        if(a[i] == a[i-1]) last[i] = last[i-1]+1;
        if(i == n || a[i] != a[i+1]){
    
    

        }
    }
    printf("%lld\n",res);
}

C. Bob in Wonderland

题意:给一棵树。每次可以选一条边断开并且把断了的其中一个结点接到任意其他结点上,问最少几次操作让它变成单链。( n ≤ 2 e 5 ) n \le 2e5) n2e5)
解题思路:最后只有两个叶子结点。并且消除一个叶子需要至少一步。答案=叶子结点个数-2

D. Crimson Sexy Jalape˜nos

交互题,告辞

E. Deep800080

题意:平面上n个点,要在一条直线上选一个点当圆心画半径R的圆,求它最多包住多少个点。 n ≤ 2 e 5 n\le 2e5 n2e5
解题思路: 以每个点为圆心画半径R的圆和直线交出线段。求出这些线段之后问题变成给n个线段求它们交的最大值。离散化之后差分可以解决。
代码是队友写的

#include<bits/stdc++.h>
#define LL long long
#define ULL unsigned LL
#define PII pair<int,int>
#define PDD pair<double,double>
#define PLL pair<LL,LL>
#define inf 0x3f3f3f3f
#define pb push_back
#define test freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#define all(x) (x).begin(),(x).end()
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn=3e5+5;
const double esp=1e-8;
int sgn(double x)
{
    
    
    if(fabs(x)<esp) return 0;
    if(x<0) return -1;
    return 1;
}
vector<int>lik[maxn];
struct Point
{
    
    
    double x,y;
    Point(double tx=0,double ty=0) {
    
    x=tx;y=ty;}
    double distance(Point p)
    {
    
    
        return hypot(x-p.x,y-p.y);
    }
    double len2(){
    
    return x*x+y*y;}
    double len(){
    
    return hypot(x,y);}
    double operator ^ (const Point &b) const {
    
    return x*b.y-y*b.x;}
    Point operator - (const Point &b) const {
    
    return Point(x-b.x,y-b.y);}
    double operator * (const Point &b) const {
    
    return x*b.x+y*b.y;}
    Point operator + (const Point &b) const {
    
    return Point(x+b.x,y+b.y);}
    Point operator / (const double &b) const {
    
    return Point(x/b,y/b);}
    Point operator * (const double &b) const {
    
    return Point(x*b,y*b);}
    Point trunc(double r)
    {
    
    
        double l=len();
        if(!sgn(l)) return *this;
        r/=l;
        return Point(x*r,y*r);
    }
}d;
struct Line
{
    
    
    Point s,e;
    Line(){
    
    };
    Line(Point _s,Point _e) {
    
    s=_s;e=_e;}
    double length() {
    
    return s.distance(e);}
    double dispointtoline(Point p)
    {
    
    
        return fabs((p-s)^(e-s))/length();
    }
    Point lineprog(Point p)
    {
    
    
        Point tt=((e-s)*((e-s)*(p-s)));
        return s+(tt/((e-s).len2()));
    }
}l;
struct circle
{
    
    
    Point p;
    double r;
    circle(){
    
    };
    circle(Point _p,double _r) {
    
    p=_p;r=_r;}
    int relationline(Line v)
    {
    
    
        double dst=v.dispointtoline(p);
        if(sgn(dst-r)<0) return 2;
        else if(sgn(dst-r)==0) return 1;
        return 0;
    }
    int pointcrossline(Line v,Point &p1,Point &p2)
    {
    
    
        if(!(*this).relationline(v)) return 0;
        Point a=v.lineprog(p);
        double d=v.dispointtoline(p);
        d=sqrt(r*r-d*d);
        if(sgn(d)==0)
        {
    
    
            p1=p2=a;
            return 1;
        }
        p1=a+(v.e-v.s).trunc(d);
        p2=a-(v.e-v.s).trunc(d);
        return 2;
    }
}c;
int n,R,A,B;
vector<PDD>a;
int b[maxn<<3];
int f[maxn<<3];
double tmp[maxn<<2];
bool cmp(double l,double r)
{
    
    
    if(r-l<=1e-4) return 0;
    return r>l;
}
void init()
{
    
    
    scanf("%d%d%d%d",&n,&R,&A,&B);
    l=Line(Point(0,0),Point(A,B));
    int x,y;
    int N=1;
    for(int i=1;i<=n;++i)
    {
    
    
        scanf("%d%d",&x,&y);
        d=Point(x,y);
        Point p1,p2;
        c=circle(d,R);
        if(c.pointcrossline(l,p1,p2))
        {
    
    
            if(p1.x>p2.x) swap(p1,p2);
            double t1=p1.x,t2=p2.x;
            if(A==0) t1=p1.y,t2=p2.y;
            if(t1>t2) swap(t1,t2);
            a.pb(PDD(t1,t2));
            tmp[N++]=t1;
            tmp[N++]=t2;
        }
    }
    sort(tmp+1,tmp+N);
    //puts("");
    for(auto i:a)
    {
    
    
        int l=lower_bound(tmp+1,tmp+N,i.first,cmp)-tmp;
        int r=lower_bound(tmp+1,tmp+N,i.second,cmp)-tmp;
        //printf("%d %d\n",l,r);
        b[l]++;
        b[r+1]--;
    }
    int C=maxn<<3,ans=0;
    for(int i=1;i<C;++i)
    {
    
    
        b[i]+=b[i-1];
        ans=max(ans,b[i]);
    }
    printf("%d\n",ans);
}
void sol()
{
    
    
}
int main()
{
    
    
    #ifdef LOCAL
    test
    #endif
    int T;
    //for(scanf("%d",&T);T;T--)
    {
    
    
        init();
        sol();
    }
	return 0;
}
/*
*/

F. Zeldain Garden

题意:给[L,R],求这个区间内数字因子数的和。
解题思路:枚举每个数作为因子的贡献,除法分块裸题。

G. Light Emitting Hindenburg

没看这题,附上队友代码

#include<bits/stdc++.h>
#define LL long long
#define ULL unsigned LL
#define PII pair<int,int>
#define PDD pair<double,double>
#define PLL pair<LL,LL>
#define inf 0x3f3f3f3f
#define pb push_back
#define test freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#define all(x) (x).begin(),(x).end()
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn=3e5+5;
int n,a[maxn],k;
void de(int nn)
{
    
    
    int aa[30];
    memset(aa,0,sizeof(aa));
    stack<int>tk;
    while(nn)
    {
    
    
        if(nn&1) tk.push(1);
        else tk.push(0);
        nn/=2;
    }
    int sz=0;
    while(!tk.empty()) aa[sz++]=tk.top(),tk.pop();
    for(int i=sz;i<=10;++i) printf("0");
    for(int i=0;i<sz;++i) printf("%d",aa[i]);
    puts("");
}
vector<int>ini;
void init()
{
    
    
    scanf("%d%d",&n,&k);
    int x;
    for(int i=0;i<n;++i)
    {
    
    
        scanf("%d",&x);
        ini.pb(x);
    }
    //sort(a,a+n);
}
int ans=0;
void dfs(vector<int>& p,int sum,int s)
{
    
    
    if(s<0) {
    
    ans=sum;return;}
    vector<int>t;
    for(auto i:p)
    {
    
    
        if(i&(1<<s)) t.pb(i);
    }
    if(t.size()>=k) {
    
    sum+=1<<s;dfs(t,sum,s-1);}
    else dfs(p,sum,s-1);
}
void sol()
{
    
    
    dfs(ini,0,29);
    printf("%d\n",ans);
}
int main()
{
    
    
    //while(cin>>n) de(n);
    #ifdef LOCAL
    test
    #endif
    int T;
    //for(scanf("%d",&T);T;T--)
    {
    
    
        init();
        sol();
    }
	return 0;
}
/*
*/

H. K==S

题意:给出q个字符串,要构造长度为n的不包含这些串的字符串,问有多少方案。
q 个 字 符 串 总 长 度 小 于 100 , n < 1 e 9 q个字符串总长度小于100,n < 1e9 q100n<1e9
解题思路: 建立AC自动机,最多有100个结点, d p ( i , j ) dp(i,j) dp(i,j)表示长度为i且最后匹配到ac自动机的结点j的方案数. a[v][u]表示状态j有多少种加1个字符方式变成状态, 则 d p ( i , j ) = ∑ x = 0 t o t a [ j ] [ x ] ∗ d p [ i − 1 ] [ x ] dp(i,j) = \sum_{x=0}^{tot} a[j][x]*dp[i-1][x] dp(i,j)=x=0tota[j][x]dp[i1][x],tot为状态总数。这个式子显然可以用矩阵快速幂优化转移。
赛场wa的教训:注意在ac自动机上标记不可用状态的时候要看整个fail树祖先上有没有不可用状态

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
using namespace std;
const int maxn = 2e5 + 50;
int fail[maxn], val[maxn];
int nxt[maxn][26];
int tot = 0;
int root;
void Insert(char *s){
    
    
    int p = root;
    int res = 0;
    while(*s){
    
    
        int x = *s - 'a';
        if(!nxt[p][x]) {
    
    
            nxt[p][x] = ++tot;
        }
        p = nxt[p][x];
        s++;
    }
    val[p] = 1;
}
queue<int> q;
void get_fail()
{
    
    
    while(q.size()) q.pop();
    for(int i = 0; i < 26; ++i) if(nxt[root][i]) q.push(nxt[root][i]);
    while(q.size()){
    
    
        int cur = q.front();q.pop();
        for(int i = 0; i < 26; ++i){
    
    
            if(nxt[cur][i]) {
    
    
                fail[ nxt[cur][i] ] = nxt[ fail[cur] ][i];
                val[nxt[cur][i]] += val[nxt[fail[cur] ][i]];
                q.push(nxt[cur][i]);
            }
            else nxt[cur][i] = nxt[fail[cur]][i];
        }
    }
}
char s[maxn];
const int mod = 1e9 +7;
int N;
struct mt{
    
    
    int a[255][255];
    mt (){
    
    fors(i,0,N) fors(j,0,N) a[i][j] = 0;}
    void E(){
    
    for(int i = 0; i < N; ++i) a[i][i] = 1;}
    void F(){
    
    for(int i = 0; i < N; ++i) for(int j = 0; j < N; ++j) a[i][j] = 1;}
    mt operator * (mt x){
    
    
        mt ans;
        for(int i = 0; i < N; ++i){
    
    
            for(int k = 0; k < N; ++k){
    
    
                if(!a[i][k]) continue;
                for(int j = 0; j < N; ++j){
    
    
                    ans.a[i][j]+=(ll)a[i][k]*x.a[k][j]%mod;
                    ans.a[i][j] %= mod;
                }
            }
        }return ans;
    }
}base;
int main()
{
    
    
    ll n;
    int m; scanf("%lld%d", &n, &m);
    while(m--){
    
    
        int x;
        scanf("%d%s", &x, s);
        Insert(s);
    }
    get_fail();
    N = tot+1;
    fors(i,0,N){
    
    
        fors(j,0,26){
    
    
            int x = nxt[i][j];
            if(val[x]) continue;
            base.a[x][i]++;
        }
    }
    mt res; res.E();
    while(n){
    
    
        if(n&1) res = res*base;
        base = base * base;
        n >>= 1;
    }
    ll ans = 0;
    fors(i,0,N){
    
    
        if(val[i] == 0) ans =(ans + res.a[i][0])%mod;
    }
    ans %= mod;
    printf("%lld\n", ans);
}

I. Ponk Warshall

题意:给出两个只包含ACGT的字符串S,T,每次可以交换S的两个字符位置,求让S变成T的最少交换次数。 n ≤ 1 e 6 n\le 1e6 n1e6
解题思路:处理出所有不对应的位置,a[i][j]表示S中字符i对着T的字符j的个数,然后优先找交换一次可以让两个位置都ok的,再找交换两次可以让3个位置都ok的,最后剩下的就是需要3次交换让4个位置ok的。枚举即可

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
using namespace std;
int ha[500];
const int maxn = 1e6 + 50;
int a[4][4];
char s[maxn], t[maxn];
int mi = 1e9;
int main()
{
    
    //ACGT
    ha['A'] = 0;
    ha['C'] = 1;
    ha['G'] = 2;
    ha['T'] = 3;
    int n;
    scanf("%s", s);
    scanf("%s", t);
    n = strlen(s);
    int ans = 0;
    fors(i,0,n){
    
    
        if(s[i] == t[i]) continue;
        a[ha[s[i]]][ha[t[i]]]++;
    }
//    fors(i,0,4){fors(j,0,4){
    
    
//        cout<<a[i][j]<<" ";
//    }cout<<endl;}
    fors(i,0,4) {
    
    
        fors(j,0,4){
    
    
            int d = min(a[i][j], a[j][i]);
            a[i][j] -= d; a[j][i] -= d;
            ans += d;
        }
    }
    fors(i,0,4){
    
    
        fors(j,0,4){
    
    
            if(i == j) continue;
            fors(k,0,4){
    
    
                if(k == i || k == j) continue;
                int d = min(a[i][j], min(a[j][k], a[k][i]));
                ans += 2*d;
                a[i][j] -= d;
                a[j][k] -= d;
                a[k][i] -= d;
            }
        }
    }
    fors(i,0,4){
    
    
        fors(j,0,4){
    
    
            if(j == i) continue;
            fors(k,0,4){
    
    
                if(k == i || k == j) continue;
                int l = i^j^k;
                int d = min(min(a[i][j], a[j][k]), min(a[k][l], a[l][i]));
                ans += 3*d;
                a[i][j] -=d;
                a[j][k] -=d;
                a[k][l] -= d;
                a[l][i] -= d;
            }
        }
    }
    cout<<ans<<endl;
}

J. Saba1000kg

题意:给一个n点e边图,每次查询给出m个点,求这m点构成子图形成连通块个数。 n , e , ≤ 1 e 5 , ∑ m ≤ 1 e 5 n,e,\le 1e5, \sum m \le 1e5 n,e,1e5,m1e5
解题思路:按照度数是否小于sqrt(n)把点分为A类和B类,其中A类度数小于sqrt(n),每次查询的时候,A类点直接访问它的所有相邻点,B类点不超过sqrt(n)个,让B类点两两之间进行判断是否相连。
每次B类点的查询如果有x个,则最多有1e5/x个这样的查询,总的复杂度不超过nsqrt(n)。判断相连那里用了set所以复杂度多个log,可以用其他手段优化,这里过了所以就没管了。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
using namespace std;
const int maxn = 1e5 + 50;
vector<int> g[maxn];
const int B = 450;
set<int> s[B];
int n, m, q;
int ha[maxn];
int fa[maxn];
int id[maxn], tot = 0;
int fnd(int x){
    
    
    if(x == fa[x]) return x;
    return fa[x] = fnd(fa[x]);
}
bool link(int x, int y){
    
    
    x = fnd(x); y = fnd(y);
    if(x == y) return false;
    fa[x] = y; return true;
}
vector<int> a;
vector<int> b;
int main()
{
    
    
    scanf("%d%d%d", &n, &m, &q);
    fors(i,1,n+1) fa[i] = i;
    fors(i,0,m){
    
    
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].pb(v);
        g[v].pb(u);
    }
    fors(i,1,n+1){
    
    
        if(g[i].size() >= B){
    
    
            id[i] = ++tot;
            for(int v: g[i]){
    
    
                s[tot].insert(v);
            }
        }
    }
    while(q--){
    
    
        a.clear();
        int k; scanf("%d", &k);
        fors(i,0,k) {
    
    
            int x; scanf("%d", &x);
            a.pb(x);
            ha[x] = 1;
        }
        b.clear();
        fors(i,0,k){
    
    
            int x = a[i];
            if(id[x] == 0){
    
    
                for(int v: g[x]){
    
    
                    if(ha[v]) link(x, v);
                }
            }else b.pb(x);
        }
        fors(i,0,b.size()){
    
    
            fors(j,i+1,b.size()){
    
    
                int u = b[i];
                int v = b[j];
                if(s[id[u]].count(v)){
    
    
                    link(u,v);
                }
            }
        }
        int ans = 0;
        fors(i,0,a.size()){
    
    
            if(fa[a[i]] == a[i]) ans++;
        }
        printf("%d\n", ans);
        fors(i,0,a.size()){
    
    
            fa[a[i]] = a[i];
            ha[a[i]] = 0;
        }
    }
}

L. The Bugs

题意:
在这里插入图片描述
在给出的序列中找出是否存在这13种大小关系的三元组。 n ≤ 2 e 5 n \le 2e5 n2e5
解题思路:写的比较麻烦的分类讨论,就按情况分析。最后对于x=z的情况再枚举x的值进行查询。总复杂度nlogn.校内OJ计算的时间是全部样例都通过的时间所以76个test跑T了,在vj上测的话是500ms。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
using namespace std;
const int maxn = 2e5 + 50;
const int inf = 1e9;
int cc[maxn], num;
int n;
int a[maxn];
int premi[maxn], premx[maxn];
int sufmi[maxn], sufmx[maxn];
int prebig[maxn], sufbig[maxn];
int prelit[maxn], suflit[maxn];
int presm[maxn], sufsm[maxn];
set<int> cur;
int tmp[13] = {
    
    123,122,132,121, 231, 112, 111, 221, 213, 212, 312, 211, 321};
int ans[13];
int mx[maxn<<2], mi[maxn<<2];
#define P pair<int,int>
void build(int rt,int l, int r){
    
    
    if(l == r) {
    
    
        mx[rt] = mi[rt] = a[l];
        return;
    }
    build(lson); build(rson);
    mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);
    mi[rt] = min(mi[rt<<1], mi[rt<<1|1]);
    return;
}
P qry(int rt,int l, int r, int L, int R){
    
    //first = mi
    if(L <= l && r <= R){
    
    
        return P(mi[rt], mx[rt]);
    }
    P tmp; tmp.first = inf; tmp.second = -inf;
    P u;
    if(L <= mid) {
    
    
        u = qry(lson, L, R);
        tmp.first = min(tmp.first, u.first);
        tmp.second = max(tmp.second, u.second);
    }
    if(R > mid){
    
    
        u = qry(rson, L, R);
        tmp.first = min(tmp.first, u.first);
        tmp.second = max(tmp.second, u.second);
    }
    return tmp;
}
int l[maxn], r[maxn];
int main()
{
    
    
    scanf("%d", &n);
    fors(i,1,n+1) scanf("%d", &a[i]), cc[++num] = a[i];
    sort(cc+1,cc+1+num);
    num = unique(cc+1,cc+1+num)-cc-1;
    fors(i,1,n+1) a[i] = lower_bound(cc+1,cc+1+num,a[i])-cc;
    premi[0] = inf;
    premx[0] = -inf;
    sufmi[n+1] = inf;
    sufmx[n+1] = -inf;
    cur.clear();
    fors(i,1,n+1){
    
    
        premi[i] = min(premi[i-1],a[i]);
        premx[i] = max(premx[i-1], a[i]);
        if(i == 1){
    
    
            prebig[i] = inf;
            prelit[i] = -inf;
            cur.insert(a[i]);
            continue;
        }
        if(cur.count(a[i])) presm[i] = 1;
        cur.insert(a[i]);

        set<int> ::iterator it = lower_bound(cur.begin(), cur.end(), a[i]);
        if(it == cur.begin()) prelit[i] = inf;
        else prelit[i] = *(--it);

        it = upper_bound(cur.begin(), cur.end(), a[i]);
        if(it == cur.end()) prebig[i] = inf;
        else prebig[i] = *it;
    }
    cur.clear();
    for(int i = n; i > 0; --i){
    
    
        sufmi[i] = min(sufmi[i+1], a[i]);
        sufmx[i] = max(sufmx[i+1], a[i]);
        if(i == n){
    
    
            sufbig[i] = inf;
            suflit[i] = -inf;
            cur.insert(a[i]); continue;
        }
        if(cur.count(a[i])) sufsm[i] = 1;

        cur.insert(a[i]);
        set<int> ::iterator it = lower_bound(cur.begin(), cur.end(), a[i]);
        if(it == cur.begin()) suflit[i] = inf;
        else suflit[i] = *(--it);

        it = upper_bound(cur.begin(), cur.end(), a[i]);
        if(it == cur.end()) sufbig[i] = inf;
        else sufbig[i] = *it;
    }
    cur.clear();
    for(int i = 2; i < n; ++i){
    
    
        if(premi[i-1] < a[i]){
    
    
            if(sufmx[i+1] > a[i]) ans[0] = 1;
            if(sufsm[i]) ans[1] = 1;
            if(sufmi[i+1] < a[i]){
    
    
                if(suflit[i] > premi[i-1]) ans[2] = 1;
                if(prelit[i] > sufmi[i+1]) ans[4] = 1;
            }
        }
        if(presm[i]){
    
    
            if(sufmx[i+1] > a[i]) ans[5] = 1;
            if(sufsm[i]) ans[6] = 1;
            if(sufmi[i] < a[i]) ans[7] = 1;
        }
        if(premx[i-1] > a[i]){
    
    
            if(sufmx[i+1] > a[i]){
    
    
                if(sufmx[i+1] > prebig[i]) ans[8] = 1;
                if(premx[i-1] > sufbig[i]) ans[10] = 1;
            }
            if(sufsm[i]) ans[11] = 1;
            if(sufmi[i+1] < a[i]) ans[12] = 1;
        }
    }
    fors(i,1,num+1) l[i] = inf, r[i] = -inf;
    fors(i,1,n+1){
    
    
        l[a[i]] = min(l[a[i]], i);
        r[a[i]] = max(r[a[i]], i);
    }
    build(1,1,n);

    fors(i,1,num+1){
    
    
        P tmp = qry(1,1,n,l[i],r[i]);
        if(tmp.first < i) ans[9] = 1;
        if(tmp.second > i) ans[3] = 1;
        if(ans[9] && ans[3]) break;
    }



    vector<int> t; t.clear();
    fors(i,0,13){
    
    
        if(ans[i]) t.pb(tmp[i]);
    }
    sort(t.begin(), t.end());
    fors(i,0,t.size()){
    
    
        printf("%d\n", t[i]);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_43202683/article/details/108233858
今日推荐