zoukankan      html  css  js  c++  java
  • NOI2015 题解

    度过了神奇的学考时间,又回到了OI战场上了...

    先刷了刷NOI2015,感觉好像不是很难的样子。

     

    Day1

    T1 程序自动分析

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4195

    唔,给你相同的条件和不相同的条件,然后要你判断是不是有矛盾...好像只要先把所有的相同的用某个神奇的数据结构弄到一起,然后看一下要求不同的里面有没有在同一个联通快里的就行了...

    这个神奇的数据结构,不就是并查集么?...

    然后由于给的数字有点大,就需要离散化一下再并查集存起来...

    好良心的送分题

    //BZOJ 4195
     
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
     
    using namespace std;
     
    const int maxn=1000010;
     
    inline int in(){
        int x=0;char ch=getchar();
        while(ch>'9' || ch<'0') ch=getchar();
        while(ch<='9' && ch>='0') x=x*10+ch-'0',ch=getchar();
        return x;
    }
     
    int n,sz,tp;
    int tmp[maxn<<1],Hash[maxn<<1];
    int p[maxn<<1];
     
    struct Node{
        int x,y;
        bool tp;
    }s[maxn];
     
    int Find_Hash(int x){
        int l=1,r=tp,mid;
        while(r-l>1){
            mid=(l+r)>>1;
            if(Hash[mid]>x) r=mid;
            else if(Hash[mid]<x) l=mid;
            else if(Hash[mid]==x) return mid;
        }
        if(Hash[l]==x) return l;
        return r;
    }
     
    bool cmp(const Node &A,const Node &B){
        return A.tp>B.tp;
    }
     
    inline int find(int x){
        int r=x,pre;
        while(p[r]!=r) r=p[r];
        while(x!=r)
            pre=p[x],p[x]=r,x=pre;
        return r;
    }
     
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("prog.in","r",stdin);
        freopen("prog.out","w",stdout);
    #endif
     
        int Kase=in();
        int fx,fy;
         
        while(Kase--){
            n=in();
             
            sz=0;
            for(int i=1;i<=n;i++){
                s[i].x=in(),s[i].y=in(),s[i].tp=in();
                tmp[++sz]=s[i].x,tmp[++sz]=s[i].y;
            }
            sort(tmp+1,tmp+sz+1);
            Hash[tp=1]=tmp[1];
            for(int i=2;i<=sz;i++)
                if(tmp[i]!=tmp[i-1]) Hash[++tp]=tmp[i];
            sort(s+1,s+n+1,cmp);
             
            for(int i=1;i<=tp;i++) p[i]=i;
            int rec=0,fool=0;
            for(int i=1;s[i].tp==1 && i<=n;i++){
                fx=find(Find_Hash(s[i].x)),fy=find(Find_Hash(s[i].y));
                p[fx]=fy;
                rec=i;
            }
            for(int i=rec+1;i<=n;i++){
                fx=find(Find_Hash(s[i].x)),fy=find(Find_Hash(s[i].y));
                if(fx==fy) {fool=1;puts("NO");break;}
            }
             
            if(!fool) puts("YES");
        }
         
        return 0;
    }
    View Code


    T2 软件包管理器

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4196

    给你一颗树,刚开始所有节点都是0状态,有两种操作:

    操作1:把点x到根的所有点全部改成1,问需要改动几个

    操作2:把点x的子树全部改成0,问需要改动几个

    唔,一个问一条链,一个问一棵子树,而且都是统一赋值,感觉就差不告诉你要打树链剖分了...然后改动几个也就是问一下改动前后的树上值的总和的变化...

    好良心的模板题

    //BZOJ 4196
     
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
     
    using namespace std;
     
    const int maxn=100010;
     
    inline int in(){
        int x=0;char ch=getchar();
        while(ch>'9' || ch<'0') ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return x;
    }
     
    struct Node{
        int data,next;
    }node[maxn];
     
    #define now node[point].data
    #define then node[point].next
     
    struct Tree{
        int sm,pt;
        Tree(){pt=-1;}
    }s[maxn*17];
     
    int n,cnt,Idex;
    int head[maxn],Sz[maxn],Son[maxn],fa[maxn];
    int id[maxn],el[maxn],hd[maxn];
     
    void addedge(int u,int v){
        node[cnt].data=v,node[cnt].next=head[u],head[u]=cnt++;
    }
     
    void dfs1(int x){
        Sz[x]=1;Son[x]=-1;
        for(int point=head[x];point!=-1;point=then){
            dfs1(now);Sz[x]+=Sz[now];
            if(Son[x]<0 || Sz[now]>Sz[Son[x]]) Son[x]=now;
        }
    }
     
    void dfs2(int x,int tp){
        id[x]=++Idex;hd[x]=tp;
        if(Son[x]<0) {el[x]=Idex;return;}
        dfs2(Son[x],tp);
        for(int point=head[x];point!=-1;point=then){
            if(now!=Son[x])
                dfs2(now,now);
        }
        el[x]=Idex;
    }
     
    void Push_down(int o,int l,int r){
        if(s[o].pt!=-1){
            int mid=(l+r)>>1;
            s[o<<1].pt=s[o].pt;s[o<<1].sm=(mid-l+1)*s[o].pt;
            s[o<<1|1].pt=s[o].pt;s[o<<1|1].sm=(r-mid)*s[o].pt;
            s[o].pt=-1;
        }
    }
     
    void Update(int o){
        s[o].sm=s[o<<1].sm+s[o<<1|1].sm;
    }
     
    void Modify(int o,int l,int r,int al,int ar,int d){
        Push_down(o,l,r);
         
        if(l==al && r==ar){s[o].pt=d,s[o].sm=(r-l+1)*d;return ;}
         
        int mid=(l+r)>>1;
         
        if(al>mid) Modify(o<<1|1,mid+1,r,al,ar,d);
        else if(ar<=mid) Modify(o<<1,l,mid,al,ar,d);
        else Modify(o<<1|1,mid+1,r,mid+1,ar,d),Modify(o<<1,l,mid,al,mid,d);
         
        Update(o);
    }
     
    int Ask(int o,int l,int r,int x){
        Push_down(o,l,r);
        if(l==r) return s[o].sm;
        int mid=(l+r)>>1;
        if(x>mid) return Ask(o<<1|1,mid+1,r,x);
        return Ask(o<<1,l,mid,x);
    }
     
    void Add(int x){
        int last;
        while(x){
            Modify(1,1,n,id[hd[x]],id[x],1);
            x=fa[hd[x]];
        }
    }
     
    void Del(int x){
        Modify(1,1,n,id[x],el[x],0);
    }
     
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("manager.in","r",stdin);
        freopen("manager.out","w",stdout);
    #endif
     
        int x,Kase,sum1,sum2;
        char ord[10];
     
        n=in();
        for(int i=1;i<=n;i++) head[i]=-1;
     
        for(int i=2;i<=n;i++)
            x=in(),addedge(x+1,i),fa[i]=x+1;
     
        dfs1(1);
        dfs2(1,1);
         
        Kase=in();
         
        while(Kase--){
    
            scanf("%s%d",ord,&x);x++;
            if(ord[0]=='i'){
                if(Ask(1,1,n,id[x])==1){puts("0");}
                else{
                    sum1=s[1].sm;
                    Add(x);
                    printf("%d
    ",s[1].sm-sum1);
                }
            }
            else{
                if(Ask(1,1,n,id[x])==0){puts("0");}
                else{
                    sum1=s[1].sm;
                    Del(x);
                    printf("%d
    ",sum1-s[1].sm);
                }
            }
        }
         
        return 0;
    }
    View Code


    T3 寿司晚宴

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4197

    有一些数字[2...n],你需要从中选出两个子集A[a1..ak],B[b1..bl],使得这两个子集中没有任何一组i,j满足gcd(ai,bj)!=1,求方案数对p的模值

    我们可以对每一个数字考虑,看能不能放到集合A或者集合B。

    如果要放到A,显然它的所有因子都不能出现在B,反之亦然。

    那么我们可以用状压表示A集合已经有了哪些质因子以及B集合已经有了哪些质因子,然后用f[i][j]表示两边的质因子控制情况,其中i&j==0

    然后对于目前元素x,如果x的质因子表示bit(x)&j==0就可以并到i中去,然后大概就是这样子的递推...

    当然这样是不行的,因为质因子个数太多了...但是我们发现小于sqrt(500)的质因子只有8个...于是我们就可以状压表示这八个质因子的掌握情况。

    首先将所有的数拆分成两个部分,一个是>sqrt(500)的质因子kind,一个是剩下的质因子所构成的一个状压表示。

    把所有数按kind分类,如果不存在>sqrt(500)的质因子那就两边都可以放,如果存在的话,对于同一个kind,只能有一个集合取,但是可以取若干个。

    那我们就可以设一个p[0][S1][S2],一个p[1][S1][S2]分别表示当前kind由A集合选和由B集合选的两种方案下,同时A集合8个质因子掌握情况为S1,B集合8个质因子掌握情况为S2的方案数。

    总的又是一个f[S1][S2]表示A集合和B集合对于8个质因子的掌握情况。

    当前加入一个新的数,若其没有>sqrt(500)的质因子或者没有他是一个新的kind分类的开端,就将p[0],p[1]先设置为没有加入这个元素前的状态f[]。然后分别枚举讨论加入后的影响。

    若其所在的kind是正在讨论的部分,那么就沿着之前留下的p[0],p[1]接着讨论。因为如果说一个kind考虑完了,f[]需要将两边的元素整合起来,也就是不同kind的考虑是独立的。所以在这个kind下两边的讨论就可以归总到f[]中去。但是p[0]和p[1]都建立在没有选这一类之前,所以要是要整合的话,需要减去在这一类kind之前的选择方案数,否则就被加了两遍。即f[]=p[0]+p[1]-f[];

    感觉思路还是挺神奇的.想到分成两个部分的质因子然后用状压的思路值得积累.

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
     
    using namespace std;
     
    const int maxn=510;
    const int Lim=(1<<8);
    const int maxl=(1<<8)+10;
    const int Small=19;
     
    int n,mod;
    int Prime[9]={0,2,3,5,7,11,13,17,19};
    int f[maxl][maxl];
    int p[2][maxl][maxl];
     
    struct Node{
        int kind,bit;
    }s[maxn];
     
    bool cmp(const Node &A,const Node &B){
        return A.kind<B.kind;
    }
     
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("dinner.in","r",stdin);
        freopen("dinner.out","w",stdout);
    #endif
     
        scanf("%d%d",&n,&mod);
         
        for(int i=2;i<=n;i++){
            s[i].kind=i;
            for(int j=1;j<=8;j++){
                if(i%Prime[j]==0){
                    s[i].bit|=(1<<j-1);
                    while(s[i].kind%Prime[j]==0) s[i].kind/=Prime[j];
                }
            }
        }
        sort(s+2,s+n+1,cmp);
         
        f[0][0]=1;
        for(int i=2;i<=n;i++){
            if(s[i].kind!=s[i-1].kind || s[i].kind==1){
                memcpy(p[0],f,sizeof(f));
                memcpy(p[1],f,sizeof(f));
            }
            for(int S1=Lim-1;S1>=0;S1--)
                for(int S2=Lim-1;S2>=0;S2--)
                if(!(S1&S2)){
                    if(!(s[i].bit&S2)) p[0][S1|s[i].bit][S2]=(p[0][S1|s[i].bit][S2]+p[0][S1][S2])%mod;
                    if(!(s[i].bit&S1)) p[1][S1][S2|s[i].bit]=(p[1][S1][S2|s[i].bit]+p[1][S1][S2])%mod; 
                }
            if(s[i].kind!=s[i+1].kind || s[i].kind==1){
                for(int S1=0;S1<Lim;S1++)
                    for(int S2=0;S2<Lim;S2++)
                        if(!(S1&S2))
                        f[S1][S2]=((p[0][S1][S2]+p[1][S1][S2]-f[S1][S2])%mod+mod)%mod;
            }
        }
         
        int ans=0;
        for(int S1=0;S1<Lim;S1++)
            for(int S2=0;S2<Lim;S2++)
                if(!(S1&S2))
                ans=(ans+f[S1][S2])%mod;
         
        printf("%d",ans);
        return 0;
    }
    View Code

     

     

    Day2

    T1 荷马史诗

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4198

    让你用一个k进制表示n个数,它们分别出现的次数为a1...an,问怎样设计这n个数既使得没有一个是另一个的前缀,又使得这个总的使用字母最短,满足前面两个的基础上,还使得最长的字母最短.

    容易让人想起哈夫曼编码.然后这个就是k进制下的,那就是k叉哈夫曼树了...

    然后要加几个0保证这棵树长得比较好看呢?...

    首先肯定是和k-1有关对吧...因为每k个变成1个,其实就是消失了k-1个.

    然后这样理解了之后,就很好弄了,首先设A=n%(k-1)看一下剩几个.因为最后要剩下一个,所以A=1正好,然后其它的就是凑成剩下一个了...

    如果A>1,那就是[(n-1)-A]+1=n-A个.如果是A<1,那就是1-A个.

    其实也不是没有把上面三个概括起来的方法: 需要+0的个数为: (n-A)%(n-1)个.

    模板题+1,良心送分.

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
     
    using namespace std;
     
    typedef long long ll;
     
    struct Node{
        ll dt;
        int lv;
         
        bool operator < (const Node &A) const{
            if(A.dt!=dt) return A.dt<dt;
            return A.lv<lv;
        }
    };
     
    int n,k;
    priority_queue<Node>q;
     
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("epic.in","r",stdin);
        freopen("epic.out","w",stdout);
    #endif
     
        ll x;
     
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%lld",&x),q.push((Node){x,1});
         
        x=n%(k-1);
        if(x==1) x=0;
        else if(x==0) x=1;
        else x=k-x;
         
        if(k==2) x=0;
         
        for(int i=1;i<=x;i++) q.push((Node){0,1});
         
        ll ans=0;
        Node now;
         
        while(!q.empty()){
            now=q.top();q.pop();
            if(q.empty()){
                printf("%lld
    %d",ans,now.lv-1);
                break;
            }
            for(int i=1;i<k;i++){
                now.dt+=q.top().dt;
                now.lv=max(now.lv,q.top().lv);
                q.pop();
            }
            now.lv++;
            ans+=now.dt;
            q.push(now);
        }
     
        return 0;
    }
    View Code

     

    T2 品酒大会

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4199

    给一个串,设定两个位置i,j为r相似是说,从它们俩开始记为1,往后走r个构成的串都是相同的.每个位置有权值,一个点对的值=它们两个位置的权值之积.

    有两问,第一问问你所有的r相似分别有多少个.第二问问你r相似的点对中最大的权值是多少.

    然后就联想到了差异这道题...感觉十分的类似.由lca的选择也就可以统计到mx[lca]相似当中去.

    这题的计数和那题也是一样的.那么求最大值和最小值联想一下也是可以想出来的.都是树形dp的一部分吧.

    但是注意每次计数就只要考虑跨过LCA的贡献,然后就能得到最长为mx[lca]相似的,

    最后再累加一下就能得到总共的了.

    原题的积累还是比较重要的.

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    inline int in(){
        int x=0,f=1;char ch=getchar();
        while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
        if(ch=='-') ch=getchar(),f=-1;
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    
    const int maxn=600010;
    const int INF=0x3f3f3f3f;
    typedef long long ll;
    
    int n,last,cnt;
    int v[maxn];
    int a[maxn][26],mx[maxn],fa[maxn];
    int rz[maxn],bg[maxn],sm[maxn],mk[maxn];
    int T[maxn],Seq[maxn];
    int mk_sm[maxn],mk_bg[maxn];
    ll rec1[maxn],rec2[maxn];
    char ch[maxn];
    
    void extend(int c,int x){
        int p=last,np=last=++cnt;
        mx[np]=mx[p]+1,rz[np]=mk[np]=1,bg[np]=sm[np]=mk_sm[np]=mk_bg[np]=v[x];
        while(!a[p][c] && p) a[p][c]=np,p=fa[p];
        if(!p) fa[np]=1;
        else{
            int q=a[p][c];
            if(mx[q]==mx[p]+1) fa[np]=q;
            else{
                int nq=++cnt; mx[nq]=mx[p]+1; bg[nq]=mk_bg[nq]=-INF; sm[nq]=mk_sm[nq]=INF;
                memcpy(a[nq],a[q],sizeof(a[q]));
                fa[nq]=fa[q];
                fa[np]=fa[q]=nq;
                while(a[p][c]==q) a[p][c]=nq,p=fa[p];
            }
        }
    }
    
    void get_order(){
        for(int i=1;i<=cnt;i++) T[mx[i]]++;
        for(int i=2;i<=n;i++) T[i]+=T[i-1];
        for(int i=1;i<=cnt;i++) Seq[T[mx[i]]--]=i;
    }
    
    void Dp(){
        int x;
        for(int i=cnt;i>=1;i--){
            x=Seq[i];
            rz[fa[x]]+=rz[x];
            bg[fa[x]]=max(bg[fa[x]],bg[x]);
            sm[fa[x]]=min(sm[fa[x]],sm[x]);
        }
        for(int i=1;i<=n;i++) rec2[i]=-0x3f3f3f3f3f3f3f3f;
        for(int i=cnt;i>=1;i--){
            x=Seq[i];
            rec1[mx[fa[x]]]+=(ll)rz[x]*mk[fa[x]];
            mk[fa[x]]+=rz[x];
            
            if(mk_bg[fa[x]]!=-INF)
                rec2[mx[fa[x]]]=max(rec2[mx[fa[x]]],(ll)mk_bg[fa[x]]*bg[x]);
            if(mk_sm[fa[x]]!=INF)
                rec2[mx[fa[x]]]=max(rec2[mx[fa[x]]],(ll)mk_sm[fa[x]]*sm[x]);
            mk_bg[fa[x]]=max(mk_bg[fa[x]],bg[x]);
            mk_sm[fa[x]]=min(mk_sm[fa[x]],sm[x]);
        }
        
        int max1=-INF,max2=-INF,min1=INF,min2=INF;
        ll ans1=(ll)n*(n-1)/2,ans2;
        for(int i=1;i<=n;i++){
            if(v[i]>=max1) max2=max1,max1=v[i];
            else if(v[i]>max2) max2=v[i];
            if(v[i]<=min1) min2=min1,min1=v[i];
            else if(v[i]<min2) min2=v[i];
        }
        
        if(max2==-INF && min2!=INF) ans2=(ll)min1*min2;
        else if(max2!=-INF && min2==INF) ans2=(ll)max1*max2;
        else ans2=max((ll)min1*min2,(ll)max1*max2);
        
        printf("%lld %lld
    ",ans1,ans2);
        for(int i=n-1;i>=1;i--)
            rec1[i]+=rec1[i+1],rec2[i]=max(rec2[i],rec2[i+1]);
        for(int i=1;i<n;i++){
            if(!rec1[i]){printf("0 0
    ");}
            else
                printf("%lld %lld
    ",rec1[i],rec2[i]);
        }
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("savour.in","r",stdin);
        freopen("savour.out","w",stdout);
    #endif
    
        last=cnt=1;
    
        n=in();
        scanf("%s",ch);
        for(int i=0;i<n;i++) v[i]=in();
        for(int i=n-1;i>=0;i--) extend(ch[i]-'a',i);
        
        get_order();    
        Dp();
    
        return 0;
    }
    View Code

     

    T3 小园丁与老司机

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4200

    空白让人思考。

  • 相关阅读:
    (转)C3P0连接池配置和实现详解
    (转)jquery插件Validate的使用
    二叉树和翻转二叉树
    利用@media screen实现网页布局的自适应
    (转)防止SQL注入的五种方法
    java代码规范
    Java泛型-类型擦除
    js中的isNaN()
    (转)sublime 一些常用功能和快捷键
    (转)java中不常见的关键字:strictfp,transient
  • 原文地址:https://www.cnblogs.com/Robert-Yuan/p/5584737.html
Copyright © 2011-2022 走看看