• NOIP模拟测试19「count·dinner·chess」


    反思:

    我考得最炸的一次

    怎么说呢?简单的两个题0分,稍难(我还不敢说难,肯定又有人喷我)42分

    前10分钟看T1,不会,觉得不可做,完全不可做,把它跳了

    最后10分钟看T1,发现一个有点用的性质,仍然认为不可实现

    0分

    所以T1是什么样的难题呢

    即使暴力也有60分,但我楞没想出来暴力怎么打

    然后我就挂掉了

     t2又是什么样难题

    大多数人秒切一个小时切两道,

    但这次考试给了我很大启迪,也正是这次考试我才开始使劲刚T1

    其实大多数T1都是比较简单的,并没有想象中那么难,这次考试对我来说意义很大

    (就在模拟测试21我也认为T1很难坚持刚其实T1还是很简单的)

    count

    题解

    一共有多少种方案可以把这棵树分成大小相同的几块

    题干简洁明了,

    性质:我们如果能分成大小相同全为$size$大小的几块,那么只有一种方案分成大小全为$size$

    有了这条性质我们就可以愉快的打了

    枚举所有n的约数打了就$AC$了(还有不要暴力枚举约数,先根号n求一下约数)

    实现,假设当前我们发现这个子树累计$size$达到了约数就剪掉这个枝条

    若减不掉就累加到父亲上,如果父亲减不掉且$size$已经比当前枚举约数大了,那么当前方案不可行,否则方案$++$

    例如我们枚举约数3,我们在3减不掉,5减不掉,累加到2,2判断size大了所以不可行

    bool dfs(ll x,ll pre,ll num){
        sz[x]=1;
        for(ll i=head[x];i;i=nxt[i]){
            ll y=ver[i];
            if(y==pre) continue;
            if(!dfs(y,x,num)) return 0;
            sz[x]+=sz[y];
            if(sz[x]>num) return 0;
        }
    //    printf("sz=%lld  
    ",sz[x]);
        if(sz[x]==num) sz[x]=0;
        return 1;
    }

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll int
    #define A 2999989
    ll sz[A],ver[A],nxt[A],head[A];
    ll ans=2,tot=1,n,m;
    vector<ll> woshishabi;
    void add(ll x,ll y){
        ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
    }
    inline ll read(){
        ll x=0,f=1;char c=getchar();
        while(!isdigit(c)){
            if(c=='-')
                f=-1;
            c=getchar();
        }
        while(isdigit(c)){
            x=x*10+c-'0';
            c=getchar();
        }
        return f*x;
    }
    bool dfs(ll x,ll pre,ll num){
        sz[x]=1;
        for(ll i=head[x];i;i=nxt[i]){
            ll y=ver[i];
            if(y==pre) continue;
            if(!dfs(y,x,num)) return 0;
            sz[x]+=sz[y];
            if(sz[x]>num) return 0;
        }
    //    printf("sz=%lld  
    ",sz[x]);
        if(sz[x]==num) sz[x]=0;
        return 1;
    }
    int main(){
        n=read();
        for(ll i=1,a,b;i<n;i++){
            a=read(),b=read();
            add(a,b);add(b,a);
        }
        for(ll i=2;i<=sqrt(n);i++){
            if(n%i==0){
                if(i*i==n) woshishabi.push_back(i);
                else
                    woshishabi.push_back(i),woshishabi.push_back(n/i);
            }
        }
        for(ll i=0;i<woshishabi.size();i++){
            if(dfs(1,0,woshishabi[i])){
                ans++;
            }
    //        printf("=%lld ans=%lld
    ",woshishabi[i],ans);
        }
        cout<<ans<<endl;
    }

    dinner

    题解

    不错的题意转化

    分成个数最少的环,每个环总权值最大的最小

    最大的最小??

    二分答案

    一个很好的二分答案题,让我明白了我们枚举其实可以拿分块优化一下

    暴力应该都会打吧

    枚举圈的数量,这里讲一下$check$

    我采取的是首先找到一个圈,找到最左可以到达的值,以及到达最左后到达右面节点,这已经$1$个圈了

    每次枚举剩下的值,若当前符合直接返回,不符合最左指针$++$,右面指针跟着移动,进行操作知道最左达到$n+1$

    这样一般过不了(然而我暴力$nian$标算了)我打法玄学

        while(now<=tim){
            tl--;
            now+=a[tl];
        }
        now-=a[tl],tl++;
        while(now<=tim){
            tr++;
            now+=a[tr];
        }
        now-=a[tr],tr--;
        //一个完美的闭合回路
        while(tl!=n+2)
        {
            cnt=2;tot=0;
    //        printf("tl=%lld tr=%lld 
    ",tl,tr);
            //除了当前满足的tl,tr之外的圈的另一半
            for(LL j=tr+1;j<=tl+n-1;j++)
            {
                if(tot>tim)tot=a[j],cnt++;
                if(cnt>m){cnt=m+10;break;}
            }
    //        printf("cnt=%lld
    ",cnt);
            if(cnt<=m)return 1;
            now-=a[tl];tl++;
            while(now<=tim){
                tr++;
                now+=a[tr];
            }
            now-=a[tr],tr--;
        }
        return 0;
    }

    主要讲剪枝

    就一句话特别简单

                if(j+t<=tl+n-1&&sum[j+t]-sum[j-1]+tot<=tim){tot+=(sum[j+t]-sum[j-1]);j+=t;continue;}

    能t加就加t,一句小的剪枝让你从$T60$分到$100$分,从$3000$-->$200$(虽然我暴力跑了$100$)

    方法简单,

    一定要掌握这种思想

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define A 1010101
    LL a[A],sum[A];
    LL n,m,ans,avg,mx=-1,t;
    LL check(LL tim){
        LL now=0,tl=n+1,tr=n+1,tot,cnt=0;
        now=a[n+1];
    //    printf("now=%lld
    ",now);
        while(now<=tim){
            tl--;
            now+=a[tl];
        }
        now-=a[tl],tl++;
        while(now<=tim){
            tr++;
            now+=a[tr];
        }
        now-=a[tr],tr--;
        //一个完美的闭合回路
        while(tl!=n+2)
        {
            cnt=2;tot=0;
    //        printf("tl=%lld tr=%lld 
    ",tl,tr);
            //除了当前满足的tl,tr之外的圈的另一半
            for(LL j=tr+1;j<=tl+n-1;j++)
            {
                if(j+t<=tl+n-1&&sum[j+t]-sum[j-1]+tot<=tim){tot+=(sum[j+t]-sum[j-1]);j+=t;continue;}
                tot+=a[j];
                if(tot>tim)tot=a[j],cnt++;
                if(cnt>m){cnt=m+10;break;}
            }
    //        printf("cnt=%lld
    ",cnt);
            if(cnt<=m)return 1;
            now-=a[tl];tl++;
            while(now<=tim){
                tr++;
                now+=a[tr];
            }
            now-=a[tr],tr--;
        }
        return 0;
    }
    int main(){
        scanf("%lld%lld",&n,&m);
        t=sqrt(n);
        for(LL i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            a[i+n]=a[i];
            mx=max(mx,a[i]);
        }for(LL i=1;i<=2*n;i++)
            sum[i]=sum[i-1]+a[i];
        LL l=mx,r=sum[n];
        while(l<=r){
            LL mid=(l+r)>>1;
            if(check(mid)) r=mid-1,ans=mid;
            else l=mid+1;
        }
        printf("%lld
    ",ans);
    }

    chess

    题解

    知道了正解也难以实现打了$4$个小时,我最后还是颓了标程,然而刚看$10$秒就明白怎么做并用$10$分钟$AC$

    首先我们要掌握一种科技最短路计数

    然而敌军不能对方案造成影响,考虑缩边

    那么题解中说缩边缩边,怎么缩啊

    我尝试跑两遍$spfa$(伪缩边)然而只有$20$分

    尝试$tarjan$(伪缩边)然而只有$0$分

    尝试对拍小点全对,大点全错

    好难实现,考虑每个点$dfs?$

    能过?,能过最多每个点搜每个点一遍善用复杂度分析$2500^2$

    那么$dfs$搜些什么,

    既然敌军不能对方案造成影响,遇到敌军往下搜但不建边,遇到空格return并且建边

    建单向边,这样我们就缩边了

    很巧妙不是吗?

    注意答案可能很大开0x7ffffffffffffffffffff

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define A 51000
    ll head[A],nxt[A],ver[A],way[A],dis[A],G[51][51],mzz[51][51];
    ll F[58][58];
    ll qix,qiy,zhongx,zhongy;
    bool flag[A];
    ll tot=0,n,m;
    ll id(ll x,ll y){
        return (x-1)*m+y;
    }
    void jb(ll x,ll y){//建边
    //    printf("jbjbjbjbx=%lld y=%lld
    ",x,y);
        nxt[++tot]=head[x],head[x]=tot,ver[tot]=y;
    }
    const ll nowx[9]={0,2,2,1,-1,-2,-2,1,-1};
    const ll nowy[9]={0,1,-1,2,2,1,-1,-2,-2};
    void dfs(ll root,ll x,ll y){
        G[x][y]=1;
    //    printf("x=%lld y=%lld
    ",x,y);
        for(ll i=1;i<=8;i++){
            ll x1=nowx[i]+x,y1=nowy[i]+y;
    //        printf("x=%lld y=%lld x1=%lld y1=%lld
    ",x,y,x1,y1);
            if(x1<1||x1>n||y1<1||y1>m||G[x1][y1]) continue;
            if(F[x1][y1]==1)
                dfs(root,x1,y1);
            else 
                G[x1][y1]=1,jb(root,mzz[x1][y1]);
        }
    }
    /*void spfa(ll w){
        deque<ll>q;
        memset(dis,0x3f,sizeof(dis));
        memset(flag,0,sizeof(flag));
        dis[w]=0;
        q.push_back(w);
        while(!q.empty()){
            ll x=q.front();
            q.pop_front();
            flag[x]=0;
            for(ll i=head[x];i;i=nxt[i]){
                ll y=ver[i];
                if(dis[y]>dis[x]+edge[i]){
                    if(edge[i]==0)
                        choose[y]=x;
                    dis[y]=dis[x]+edge[i];    
    //                printf("x=%lld y=%lld dx=%lld dy=%lld
    ",x,y,dis[x],dis[y]);
    //                if(x==59||y==59){
    //                    printf("***********
    ");
    //                }
                    if(!flag[y]){
                        q.push_back(y);
                        flag[y]=1;
                    }
                }
            }
        }
    }*/
    void spfa2(ll w){
        deque<ll>q;
        memset(dis,0x7f,sizeof(dis));
        memset(flag,0,sizeof(flag));
        dis[w]=0;
        q.push_back(w);
        while(!q.empty()){
            ll x=q.front();
            q.pop_front();
            flag[x]=0;
    //        printf("x=%lld
    ",x);
            for(ll i=head[x];i;i=nxt[i]){
                ll y=ver[i];
                if(dis[y]>dis[x]+1){
                    way[y]=way[x];
                    dis[y]=dis[x]+1;
                    if(!flag[y]){
                        q.push_back(y);
                        flag[y]=1;
                    }
                }
                else if(dis[y]==dis[x]+1){
                    way[y]+=way[x];
                }
            }
            
        }
    }
    int main(){
    
        scanf("%lld%lld",&n,&m);    
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=m;j++){
                mzz[i][j]=id(i,j);
    //            printf("mzz[%lld][%lld]=%lld
    ",i,j,mzz[i][j]);
            }
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=m;j++){
                scanf("%lld",&F[i][j]);            
                if(F[i][j]==3)
                    qix=i,qiy=j;
                else if(F[i][j]==4)
                    zhongx=i,zhongy=j;
            }
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=m;j++){
                if(F[i][j]==0||F[i][j]==3){
                    memset(G,0,sizeof(G));
                    dfs(mzz[i][j],i,j);
                }
            }        
    /*    for(ll i=1;i<=n;i++)
            for(ll j=1;j<=m;j++){        
                if(F[i][j]==2) continue;
                for(ll k=1;k<=8;k++){
                    ll x1=i+nowx[k],y1=j+nowy[k];
                    ll idpre=id(i,j),idnow=id(x1,y1);
                    if(x1<1||x1>n||y1<1||y1>m) continue;
                    if(F[x1][y1]==2) continue;
                    if(!G[idpre][idnow]){
                        if(F[x1][y1]==1||F[x1][y1]==4)
                            jb(idpre,idnow,0),G[idpre][idnow]=1;
                        else
                            jb(idpre,idnow,1),G[idpre][idnow]=1;
                    }
                }
                if(F[i][j]==3)
                    qix=i,qiy=j;
                else if(F[i][j]==4)
                    zhongx=i,zhongy=j;
        }
    */    way[mzz[qix][qiy]]=1;
    //    printf("%lld
    %lld
    ",dis[id(zhongx,zhongy)],way[id(zhongx,zhongy)]);
        spfa2(mzz[qix][qiy]);
        if(dis[mzz[zhongx][zhongy]]>1e8) printf("-1
    ");
        else
        printf("%lld
    %lld
    ",dis[mzz[zhongx][zhongy]]-1,way[mzz[zhongx][zhongy]]);
    }
  • 相关阅读:
    数据结构-串的堆分配存储
    ServerSocket
    Java-坦克大战
    Java-输入输出流
    MyEclipse 快捷键
    数据结构-串的定长顺序存储
    我的软考资料集合
    软考中级软件设计师考试大纲
    2016年第一季度阅读书目
    中国大陆开源镜像网站汇总
  • 原文地址:https://www.cnblogs.com/znsbc-13/p/11366081.html
Copyright © 2020-2023  润新知