• PKUSC 模拟赛 day2 下午总结


    终于考完了,下午身体状况很不好,看来要锻炼身体了,不然以后ACM没准比赛到一半我就挂掉了

    下午差点AK,有一道很简单的题我看错题面了所以没有A掉

    第一题显然是非常丝薄的题目

    我们很容易通过DP来O(n^2)的求出深度至多为k的方案

    然后我们很容易通过DP来O(n^2)的求出深度至多为k-1的方案

    转移的时候分当前放左括号或者右括号讨论就可以了

    两个作差就是答案了,单次询问时间复杂度O(n^2)

    如果给dp加一维O(n^3)预处理,O(1)回答

    当然要写高精度

    成功抢到下午的first blood,请叫我手速狗,汪

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
     
    const int mod=10000;
    int n,k,kase;
    struct big_num{
        int a[22],len;
        void add(const big_num &A){
            len=max(A.len,len)+1;
            for(int i=1;i<=len;++i)a[i]=a[i]+A.a[i];
            for(int i=1;i<=len;++i){
                a[i+1]+=a[i]/mod;
                a[i]%=mod;
            }
            while(len>0&&a[len]==0)len--;
        }
        void minus(const big_num &A){
            for(int i=1;i<=len;++i){
                if(a[i]<A.a[i])a[i+1]--,a[i]=a[i]+mod-A.a[i];
                else a[i]=a[i]-A.a[i];
            }
            while(len>0&&a[len]==0)len--;
        }
        void print(){
            printf("Case %d: ",kase);
            printf("%d",a[len]);
            for(int i=len-1;i>=1;--i)printf("%04d",a[i]);
            printf("
    
    ");
        }
        void clear(){len=1;memset(a,0,sizeof(a));}
    }f[102][52],ans;
     
    int main(){
        while(scanf("%d%d",&n,&k)==2){
            if(!n&&!k)break;
            f[0][0].len=1;f[0][0].a[1]=1;
            n<<=1;kase++;
            for(int i=1;i<=n;++i){
                for(int j=0;j<=k;++j){
                    f[i][j].clear();
                    if(j>0)f[i][j].add(f[i-1][j-1]);
                    if(j<k)f[i][j].add(f[i-1][j+1]);
                }
            }
            ans=f[n][0];
            for(int i=1;i<=n;++i){
                for(int j=0;j<=k-1;++j){
                    f[i][j].clear();
                    if(j>0)f[i][j].add(f[i-1][j-1]);
                    if(j<k-1)f[i][j].add(f[i-1][j+1]);
                }
            }
            ans.minus(f[n][0]);
            ans.print();
        }return 0;
    }
    

    第二题保证每个点恰好属于一个环,求最长环

    我能想到的有两种做法:

    1、这题目显然是点双的裸题QAQ

    2、用并查集建树,之后枚举非树边计算环的大小更新答案

    由于上午点双的阴影还在,所以果断写了第二种做法

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
     
    const int maxn=5010;
    int T,n,m;
    int f[maxn];
    int h[maxn],cnt=0;
    bool check[maxn];
    struct edge{
        int to,next;
    }G[3000010];
    struct Edge{
        int u,v;
    }c[3000010];
    int fa[maxn],dep[maxn];
    int anc[maxn][20];
     
    void read(int &num){
        num=0;char ch=getchar();
        while(ch<'!')ch=getchar();
        while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
    }
    void add(int x,int y){
        ++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt;
    }
    int ufs(int x){
        return f[x]==x?x:f[x]=ufs(f[x]);
    }
    void DFS(int u,int f){
        fa[u]=f;
        for(int i=h[u];i;i=G[i].next){
            int v=G[i].to;
            if(v==f)continue;
            dep[v]=dep[u]+1;
            DFS(v,u);
        }return;
    }
    void pre_LCA(){
        for(int i=1;i<=n;++i){
            anc[i][0]=fa[i];
            for(int j=1;(1<<j)<=n;++j)anc[i][j]=-1;
        }
        for(int j=1;(1<<j)<=n;++j){
            for(int i=1;i<=n;++i){
                if(anc[i][j-1]!=-1){
                    int a=anc[i][j-1];
                    anc[i][j]=anc[a][j-1];
                }
            }
        }return;
    }
    int LCA(int p,int q){
        if(dep[p]<dep[q])swap(p,q);
        int log;
        for(log=0;(1<<log)<=dep[p];++log);--log;
        for(int i=log;i>=0;--i){
            if(dep[p]-(1<<i)>=dep[q])p=anc[p][i];
        }
        if(p==q)return p;
        for(int i=log;i>=0;--i){
            if(anc[p][i]!=-1&&anc[p][i]!=anc[q][i]){
                p=anc[p][i];q=anc[q][i];
            }
        }return fa[p];
    }
     
    int main(){
        read(T);
        while(T--){
            read(n);read(m);
            memset(h,0,sizeof(h));cnt=0;
            for(int i=1;i<=n;++i)f[i]=i,dep[i]=0;
            for(int i=1;i<=m;++i)check[i]=false;
            for(int i=1;i<=m;++i){
                read(c[i].u);read(c[i].v);
                int d1=ufs(c[i].u),d2=ufs(c[i].v);
                if(d1!=d2){
                    f[d1]=d2;
                    add(c[i].u,c[i].v);
                    add(c[i].v,c[i].u);
                    check[i]=true;
                }
            }
            for(int i=1;i<=n;++i)if(ufs(i)==i)DFS(i,-1);
            pre_LCA();
            int ans=0;
            for(int i=1;i<=m;++i){
                if(!check[i]){
                    int lca=LCA(c[i].u,c[i].v);
                    ans=max(ans,dep[c[i].u]+dep[c[i].v]-2*dep[lca]+1);
                }
            }printf("%d
    ",ans);
        }return 0;
    }
    

    第三题是原题QAQ

    我背后缀数组模板的时候背的题目

    曾经跟lyc说过这道题目可以用SAM做,然后是论文题

    然而SAM的极限时间复杂度是O(n*sqrt(n)),感觉多笔测资有些虚

    考试后发现他们都写得SAM QAQ 貌似可以卡住的样子 QAQ

    后缀数组码码码,明显后缀数组很不熟练,写了30min才写完

    比其他模板写的慢多了QAQ 然后除了被系统卡了rank的关键字之外1A

    真是舒爽

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=1000010;
    int n,L,R,kase;
    int s[maxn],now;
    int cnt=-1;
    int pos[maxn];
    int wa[maxn],wb[maxn],w[maxn],wv[maxn];
    int sa[maxn],height[maxn],rk[maxn];
    int vis[1010],tim;
    void get(int id){
        char ch=getchar();
        while(ch<'!')ch=getchar();
        while(ch>='a'&&ch<='z'){s[++cnt]=(int)(ch)+200;pos[cnt]=id;ch=getchar();}
        s[++cnt]=++now;
    }
    bool cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
    void get_da(int n,int m){
        int *x=wa,*y=wb,*t;
        for(int i=0;i<m;++i)w[i]=0;
        for(int i=0;i<n;++i)w[x[i]=s[i]]++;
        for(int i=1;i<m;++i)w[i]+=w[i-1];
        for(int i=n-1;i>=0;--i)sa[--w[x[i]]]=i;
        for(int p=1,j=1;p<n;j<<=1,m=p){
            p=-1;
            for(int i=n-j;i<n;++i)y[++p]=i;
            for(int i=0;i<n;++i)if(sa[i]>=j)y[++p]=sa[i]-j;
            for(int i=0;i<n;++i)wv[i]=x[y[i]];
            for(int i=0;i<m;++i)w[i]=0;
            for(int i=0;i<n;++i)w[wv[i]]++;
            for(int i=1;i<m;++i)w[i]+=w[i-1];
            for(int i=n-1;i>=0;--i)sa[--w[wv[i]]]=y[i];
            t=x;x=y;y=t;x[sa[0]]=0;p=1;
            for(int i=1;i<n;++i)x[sa[i]]=cmp(t,sa[i],sa[i-1],j)?p-1:p++;
        }
        for(int i=0;i<n;++i)rk[sa[i]]=i;
        int k=0;
        for(int i=0;i<n;++i){
            if(rk[i]==0){height[rk[i]]=0;continue;}
            if(k)--k;
            int j=sa[rk[i]-1];
            while(s[j+k]==s[i+k])k++;
            height[rk[i]]=k;
        }return;
    }
    bool check(int blo){
        int tot=0;
        for(int i=n;i<=cnt;++i){
            if(height[i]<blo){
                tim++;tot=1;
                vis[pos[sa[i]]]=tim;
            }
            else{
                if(vis[pos[sa[i]]]!=tim)vis[pos[sa[i]]]=tim,tot++;
                if(tot>n/2)return true;
            }
        }return false;
    }
    void print(){
        if(L==0){printf("?
    ");return;}
        int tot=0;
        for(int i=n;i<=cnt;++i){
            if(height[i]<L){
                tim++;
                if(tot>n/2){
                    for(int j=sa[i-1];j<=sa[i-1]+L-1;++j)putchar(s[j]-200);
                    printf("
    ");
                }tot=1;vis[pos[sa[i]]]=tim;
            }else{
                if(vis[pos[sa[i]]]!=tim)vis[pos[sa[i]]]=tim,tot++;
            }
        }
        if(tot>n/2){
            for(int j=sa[cnt];j<=sa[cnt]+L-1;++j)putchar(s[j]-200);
            printf("
    ");
        }return;
    }
    int main(){
        while(scanf("%d",&n)==1){
            if(n==0)break;
            if(kase)printf("
    ");
            kase++;cnt=-1;now=0;
            for(int i=1;i<=n;++i)get(i);
            get_da(cnt+1,1000);
            memset(vis,0,sizeof(vis));tim=1;
            L=0,R=cnt;
            while(L<R){
                int mid=L+(R-L+1)/2;
                if(check(mid))L=mid;
                else R=mid-1;
            }
            print();
        }return 0;
    }
    

    第四题好难读的题面,然后读懂了就很容易发现是个斗地主的弱化版

    随便写写就好啦

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
     
    int T,kase;
    char s[22][22];
    char t[22][22];
    int vis[4][12];
    int tmp[1010];
     
    bool DFS(int num){
        if(num==12){
            for(int i=1;i<=3;++i){
                for(int j=1;j<=9;++j){
                    if(vis[i][j]==2)return true;
                }
            }return false;
        }
        for(int i=1;i<=3;++i){
            for(int j=1;j<=9;++j){
                if(vis[i][j]){
                    if(vis[i][j]>=3){
                        vis[i][j]-=3;
                        if(DFS(num+3))return true;
                        vis[i][j]+=3;
                    }
                    if(vis[i][j+1]&&vis[i][j+2]){
                        vis[i][j]--;
                        vis[i][j+1]--;
                        vis[i][j+2]--;
                        if(DFS(num+3))return true;
                        vis[i][j]++;
                        vis[i][j+1]++;
                        vis[i][j+2]++;
                    }
                }
            }
        }return false;
    }
     
    bool check(){
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=14;++i)vis[tmp[s[i][2]]][s[i][1]-'0']++;
        for(int i=1;i<=3;++i){
            for(int j=1;j<=9;++j){
                if(vis[i][j]>4)return false;
            }
        }
        if(DFS(0))return true;
        return false;
    }
     
    int main(){
        scanf("%d",&T);
        tmp['s']=1;tmp['b']=2;tmp['c']=3;
        while(T--){
            kase++;int cnt=0;
            for(int i=1;i<=13;++i)scanf("%s",s[i]+1);
            printf("Case %d:",kase);
            for(int i=1;i<=3;++i){
                if(i==1)s[14][2]='s';
                else if(i==2)s[14][2]='b';
                else s[14][2]='c';
                for(int j=1;j<=9;++j){
                    s[14][1]=j+'0';
                    if(check()){
                        printf(" ");cnt++;
                        putchar(s[14][1]);
                        putchar(s[14][2]);
                    }
                }
            }
            if(!cnt)printf(" None");
            printf("
    ");
        }return 0;
    }
    

    第五题考试的时候一直看错题,然后就没有A掉

    后来听lyc说了题意之后发现是个背包的丝薄题

    看对题之后随便写写就A啦

    正确性的证明是因为答案是个二次函数QAQ

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
      
    typedef long long LL;
    const int maxn=110;
    const LL oo=1LL<<60;
    int n,L;
    LL sa,sc;
    int c[maxn],a[maxn];
    LL f[maxn][maxn][502];
      
    int main(){
        scanf("%d%d",&n,&L);
        for(int i=1;i<=n;++i)scanf("%d",&a[i]),sa+=a[i];
        for(int i=1;i<=n;++i)scanf("%d",&c[i]),sc+=c[i];
        for(int i=0;i<=n;++i)for(int j=0;j<=n;++j)for(int k=0;k<=sa;++k)f[i][j][k]=oo;
        f[0][0][0]=0;
        for(int i=1;i<=n;++i){
            for(int j=0;j<=n;++j){
                for(int k=0;k<=sa;++k){
                    f[i][j][k]=f[i-1][j][k];
                    if(k>=a[i]&&j>=1)f[i][j][k]=min(f[i][j][k],f[i-1][j-1][k-a[i]]+c[i]);
                }
            }
        }
        double ans=1e13;
        for(int i=1;i<=n;++i){
            if(i==L||n-i==L){
                for(int k=1;k<=sa-1;++k){
                    if(f[n][i][k]!=oo){
                        double now=(double)(f[n][i][k])/(double(k))*(double)(sc-f[n][i][k])/(double)(sa-k);
                        ans=min(ans,now);
                    }
                }
            }
        }printf("%.3lf
    ",ans);
        return 0;
    }
    

    第六题正解是维护凸包,每次二分斜率求最优值做DP就可以了

    然后考试的时候没有太多时间,很容易发现答案是fi+ci*abs(xi-xj)的形式

    两边项次数都是一次

    下面不加证明的给出以下结论:

    1、当两边项都是二次的时候,使用KD_Tree并定期重构保证不退化时间复杂度较优

    2、当两边项一个一次一个二次的时候,使用平衡树以二次项为键值维护并做KD_Tree的查询较优

    3、当两边项均为一次时,使用线段树同时维护两个信息并做KD_Tree的查询较优

    然后本题显然两个一次直接线段树+KD_Tree就可以啦

    其实这个题目是式子是省选模拟某套题目的第一题的式子?

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
     
    typedef long long LL;
    const int maxn=100010;
    const int oo=0x7fffffff;
    int n;
    int x[maxn],c[maxn],t[maxn];
    int l[maxn<<2],r[maxn<<2];
    LL mxd[maxn<<2],mxc[maxn<<2];
    LL dp[maxn],now,k;
     
    void UPD(int o,int L,int R,int p,LL d,LL c,int x){
        if(L==R){mxd[o]=d;mxc[o]=c;l[o]=x;r[o]=x;return;}
        int mid=(L+R)>>1;
        if(p<=mid)UPD(o<<1,L,mid,p,d,c,x);
        else UPD(o<<1|1,mid+1,R,p,d,c,x);
        mxd[o]=max(mxd[o<<1],mxd[o<<1|1]);
        mxc[o]=max(mxc[o<<1],mxc[o<<1|1]);
        l[o]=min(l[o<<1],l[o<<1|1]);
        r[o]=max(r[o<<1],r[o<<1|1]);
    }
    void ask(int o,int L,int R,int x,int y){
        if(L>=x&&R<=y){
            if(L==R)now=max(now,mxd[o]+mxc[o]*abs(l[o]-k));
            else{
                LL d1=mxd[o<<1]+mxc[o<<1]*max(abs(l[o<<1]-k),abs(r[o<<1]-k));
                LL d2=mxd[o<<1|1]+mxc[o<<1|1]*max(abs(r[o<<1|1]-k),abs(l[o<<1|1]-k));
                int mid=(L+R)>>1;
                if(d1>d2){
                    if(d1>now)ask(o<<1,L,mid,x,y);
                    if(d2>now)ask(o<<1|1,mid+1,R,x,y);
                }else{
                    if(d2>now)ask(o<<1|1,mid+1,R,x,y);
                    if(d1>now)ask(o<<1,L,mid,x,y);
                }
            }return;
        }
        int mid=(L+R)>>1;
        if(y<=mid)ask(o<<1,L,mid,x,y);
        else if(x>mid)ask(o<<1|1,mid+1,R,x,y);
        else ask(o<<1,L,mid,x,y),ask(o<<1|1,mid+1,R,x,y);
    }
     
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)scanf("%d%d%d",&x[i],&c[i],&t[i]);
        for(int i=1;i<=(n<<2);++i)l[i]=oo,r[i]=-oo;
        UPD(1,1,n,1,0,c[1],x[1]);
        for(int i=2;i<=n;++i){
            now=0;k=x[i];
            ask(1,1,n,1,i-1);
            dp[i]=now+t[i];
            UPD(1,1,n,i,dp[i],c[i],x[i]);
        }printf("%lld
    ",dp[n]);
        return 0;
    }
    

    今天下午发挥的较为不错

    直接说缺点吧:

    1、第三题的后缀数组模板不熟练,拖了一些时间

    2、身体素质不太好,在四点半以后就开始狂吐不止,没有什么精力去仔细看第五题,导致第五题并没有A掉

  • 相关阅读:
    方法参数个数最多不宜超过4个
    避免方法中使用大量局部变量
    JQuery学习备忘
    CSS学习备忘
    解析Path方法备忘
    获取差集合的一种实现思路
    前台JSP页面独立化
    requireJs的使用
    handlebar
    移动端h5<a>标签点击样式去除
  • 原文地址:https://www.cnblogs.com/joyouth/p/5502888.html
Copyright © 2020-2023  润新知