zoukankan      html  css  js  c++  java
  • [3.16校内训练赛]

    这次一个学长出题....结果我把dij写成了大顶的,就说复杂度那么科学怎么T了.........真的丢人

    -------------------------------

    A.给定一个长度为n的序列,你要求出从那个位置开始连续数n个数,得到的序列最大(先比第一位,再第二位..)。n<=2000000

    题解:第一眼想到的是可以把每个数拆开来计数排序+dc3后缀数组,应该可过。

    但是此题还有一个非常妙的解法。

    假设目前最优的开头是i,你要判断开头为j的是否更优,那么你可以找到第一位不同的位k,即s[i+k]!=s[j+k],

    这时候,如果s[j+k]<[i+k],那么以i开头的比j优,i+1的也会比j+1的优.....i+k的同样比j+k更优,所以我们可以直接让j=j+k+1,继续计算即可。复杂度O(n)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 5000000
    using namespace std;
    inline int read()
    {
        int  x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n;char s[2*MAXN+5];
        
    int main()
    {
        n=read();
        int k,i=1,j=2;
        for(int i=1;i<=n;++i) do s[i]=getchar(); while(s[i]<'a'||s[i]>'z');
        for(int i=1;i<=n;++i) s[i+n]=s[i];
        for(;j<=n;)
        {
            for(k=0;s[j+k]==s[i+k]&&k<n;k++);
            if(s[j+k]>s[i+k])i=i+k+1,j=i+1;   
              else  j=j+k+1;
        }
        printf("%d ",i-1);
        return 0;
    }

    B.有n个数s1..sn,你要从中删除一些数,使得最后满足si=i(第i个数是i)的数尽量多。n<=100000

    做法:考虑一个n^2的dp,即f[i]=max(f[j])+1,但必须满足 1)i>j   2)si-sj<=i<j  3)si>sj

    但是我们可以发现,如果满足了2和3两个条件,那么第一个条件一定满足。

    又因为si-sj<=i-j   -> si-i<=sj-j 所以我们把si-i和si其中的一个排序,并且把另一个离散之后存到线段树里加速dp就好啦。

    复杂度nlogn

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define N 131072
    using namespace std;
    inline int read()
    {
        int  x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n;
    int s[100005],f[100005],l[100005],p[100005],T[N*2+5],l2[100005];
    
    void renew(int x,int ad)
    {
        x+=N;T[x]=max(T[x],ad);
        for(x>>=1;x;x>>=1)T[x]=max(T[x<<1],T[x<<1|1]);
    }
    
    int query(int l,int r)
    {
        int sum=0;
        for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
        {
            if(~l&1)sum=max(sum,T[l+1]);
            if( r&1)sum=max(sum,T[r-1]);    
        }
        return sum;
    }
    
    bool cmp(int x,int y)
    {
        return s[x]<s[y]||(s[x]==s[y]&&p[x]<p[y]);
    }
    
    int main()
    {
        freopen("fivethree.in","r",stdin);
        freopen("fivethree.out","w",stdout);
        n=read();
        for(int i=1;i<=n;i++)s[i]=read();
        for(int i=1;i<=n;i++)l2[i]=p[i]=s[i]-i;
        sort(l2+1,l2+n+1);int j=1;
        for(int i=2;i<=n;i++)if(l2[i]!=l2[i-1]) l2[++j]=l2[i];
        for(int i=1;i<=n;i++)p[i]=j-(lower_bound(l2+1,l2+j+1,p[i])-l2)+1;
        int K=0;
        for(int i=1;i<=n;i++)
            if(s[i]-i<=0) l[++K]=i;
        sort(l+1,l+K+1,cmp);
        s[0]=-2000000000;
        for(int ii=1;ii<=K;ii++)
        {
            int i=l[ii];
            f[i]=query(1,p[i])+1;
            if(s[i]!=s[l[ii+1]])
                for(int j=ii;j&&s[l[j]]==s[l[ii]];j--)
                    renew(p[l[j]],f[l[j]]);
        }    
        printf("%d
    ",query(1,n));
        return 0;
    }

    C.有一个n个点m条边的图,有边权,你要从第1个点到第n个点,但是你必须先到2...k+1这些点去搞事情。特殊地,有一些限制(a,b),表示必须在a搞完事情之后才能在b搞事情,求搞完所有事情到达第n个点至少要走的距离。n<=20000,m<=200000,k<=20

    题解:很容易发现其实和图没什么关系,而且k的范围比较小,考虑壮压dp。

    先用dij预处理出两两之间和每个点到起点终点的距离

    然后用f[i][j]表示最后走到第i个点,状态是j的最小距离,转移即可。状态数最多2^20*20≈20000000,转移复杂度20,但实际并没有这么多状态,并且题目有2s,所以还是比较科学的。

    交了一个大顶堆,真的丢人

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #define INF 2100000000
    using namespace std;
    inline int read()
    {
        int  x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n,m,K,cnt=0;
    int p[25],l[25],to,v[25],rk[1000005],num[1000005];
    int dis[25][25],d[20005],head[20005];
    int f[22][1100000];
    bool mark[20005];
    struct edge{
        int to,next,w;
    }e[400005];
    struct node{int x,f;};
    class cmp{public:bool operator()(node a,node b){return a.f>b.f;}};
    priority_queue<node,vector<node>,cmp>q;
    
    void ins(int f,int t,int w)
    {
        e[++cnt].next=head[f];head[f]=cnt;
        e[cnt].to=t;e[cnt].w=w;
    }
    
    void work(int num,int from)
    {
        while(!q.empty())q.pop(); 
        memset(d,0x3f3f3f,sizeof(d));
        memset(mark,0,sizeof(mark));
        d[from]=0;q.push((node){from,0});
        while(!q.empty())
        {
            int u=q.top().x;q.pop();if(mark[u])continue;mark[u]=1;
            for(int i=head[u];i;i=e[i].next)
                if(d[u]+e[i].w<d[e[i].to])
                {
                    d[e[i].to]=d[u]+e[i].w; 
                    q.push((node){e[i].to,d[e[i].to]});        
                }    
        }
        for(int i=1;i<=K;i++)dis[num][i]=d[i+1];
        dis[num][K+1]=d[n];
    }
    
    int main()
    {
        freopen("revenge.in","r",stdin);
        freopen("revenge.out","w",stdout);
        n=read();m=read();K=read();to=1<<K;
        p[1]=1;for(int i=2;i<=K;i++)p[i]=p[i-1]<<1;
        for(int i=1;i<=m;i++)
        {
            int u=read(),v=read(),w=read();
            ins(u,v,w);ins(v,u,w);
        }
        m=read();
        for(int i=1;i<=m;i++)
        {int u=read(),v=read();l[v-1]|=p[u-1];}
        work(0,1);if(K==0)return 0*printf("%d",dis[0][K+1]);
        for(int i=1;i<=K;i++)
            work(i,i+1);
        for(int i=1;i<=K;i++)
            for(int j=0;j<to;j++)
                f[i][j]=INF;
        for(int i=1;i<=K;i++) if(!l[i])
            f[i][p[i]]=dis[0][i];
        for(int i=1;i<to;i++)
        {
            for(int j=1;j<=K;j++)
                if(f[j][i]<INF)
                    for(int k=1;k<=K;k++)
                        if((l[k]&i)==l[k]&&(!(i&p[k])))
                        {
                            int into=i|p[k];
                            f[k][into]=min(f[k][into],f[j][i]+dis[j][k]);
                        }
        }
        int ans=INF;
        for(int i=1;i<=K;i++)ans=min(ans,f[i][to-1]+dis[i][K+1]);
        cout<<ans;
        return 0;
    }

    D.有n个数,你可以任选一个区间,并且用其中的任意一个数与这个区间的次大值异或起来,求最大异或值。n<=50000

    题解:这道题是最好想出来的.....

    对于每个数,往两边二分到第一个比它大的数,再向两边二分到第二个比它大的数。

    假设序列是1 3 4 2 6 1 8,那么对于数2,它二分到的四个点分别是3,4,6,8,假设称为l2,l1,r1,r2

    那么在区间(l1,r2)和(l2,r1)内,这个数显然都是次大值。然后我们对于每个查询到的区间都在可持久化TRIE树上查询一个和它异或最大的值,更新一下答案。

    二分时候可以用线段树来check,在可持久化TRIE上查询是log,所以复杂度是nlog^2n

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 2000005
    #define N 65536
    #define INF 2100000000
    using namespace std;
    inline int read()
    {
        int  x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int s[50005];
    struct TRIE{
        int son[2];
        int size;
    }T[MAXN];
    int t[N*2+5],c[35],rt[50005],cnt=0,n,ans=0;
    
    void renew(int x,int ad)
    {
        t[x+=N]=ad;
        for(x>>=1;x;x>>=1)t[x]=max(t[x<<1],t[x<<1|1]);
    }
    
    int query(int l,int r)
    {
        int sum=0;
        for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
        {
            if(~l&1)sum=max(sum,t[l+1]);
            if( r&1)sum=max(sum,t[r-1]);    
        }
        return sum;
    }
    
    void ins(int num)
    {
        memset(c,0,sizeof(c));rt[num]=++cnt;
        for(int x=s[num],len=0;x;c[++len]=(x&1),x>>=1);
        for(int i=30,x=rt[num-1],nx=rt[num];i;i--)
        {
            T[nx].son[c[i]]=++cnt;T[nx].son[!c[i]]=T[x].son[!c[i]];
            x=T[x].son[c[i]];nx=T[nx].son[c[i]];
            T[nx].size=T[x].size+1;
        }
    }
    
    void calc(int l,int r,int xx)
    {
        if(l>r)return;
        memset(c,0,sizeof(c));for(int x=xx,len=0;x;c[++len]=(x&1),x>>=1);
        int sum=0;
        for(int i=rt[l-1],j=rt[r],k=30,l=1<<29;k;k--,l>>=1)
        {
            c[k]=!c[k];
            int to=(T[T[j].son[c[k]]].size-T[T[i].son[c[k]]].size>0)?c[k]:1-c[k];
            if(to)sum|=l;j=T[j].son[to];i=T[i].son[to];    
        //    cout<<i<<" "<<j<<" "<<to<<" "<<sum<<endl;
        }
        ans=max(ans,sum^xx);
    }
    
    int get(int rr,int xx,int spec)
    {
        int l=1,mid,r=--rr,q=rr+1;
        if(!rr)return spec;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(query(mid,rr)<xx)q=mid,r=mid-1;
            else l=mid+1;
        }
        return q;
    }
    
    int get2(int ll,int xx,int what)
    {
        int l=++ll,r=n,mid,q=ll-1;
        if(ll>n)return what;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(query(ll,mid)<xx)q=mid,l=mid+1;
            else r=mid-1;    
        }
        return q;
    }
    
    int main()
    {
        freopen("lock.in","r",stdin);
        freopen("lock.out","w",stdout);
        n=read();int mx=0;
        for(int i=1;i<=n;i++)
            {s[i]=read();ins(i);renew(i,s[i]);mx=max(mx,s[i]);}
        for(int i=1;i<=n;i++)
        {
            if(s[i]==mx)continue;
            int l1=get(i,s[i],2)-1,l2=get(l1,s[i],l1);
            int r1=get2(i,s[i],n-1)+1,r2=get2(r1,s[i],r1);
        //    cout<<i<<" "<<l1<<" "<<l2<<" "<<r1<<" "<<r2<<"!!!!!!!"<<endl;
            calc(l2,min(r1+1,n),s[i]);calc(max(0,l1-1),r2,s[i]);
        }
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    山屋惊魂 · 大模拟 · 码长破千 · 祭
    CSP-S 模拟92
    CSP-S 模拟91
    关于我
    CSP-S 模拟76
    [2020 年联考 A 卷] HEOI 2020 退役记
    csp-s 2019 退役记
    论人贵有自知之明的重要性
    [NOI2018] 你的名字
    HDU 3446 daizhenyang's chess
  • 原文地址:https://www.cnblogs.com/FallDream/p/xunlian316.html
Copyright © 2011-2022 走看看