部分noip题解

NOIP2011

Mayan游戏

模拟题

比较有意思的搜索,需要剪枝

无脑枝:1.只有两个方块时直接return(不可能再消掉了) 2.不换颜色相同的

有脑枝:发现左移和右移其实是一样的,当前块与右面块交换等于右面块与当前块交换

然而左面为空时左移右移不同,特判一下

(其实就是向左移只当左面为空时才移,其他任意一种情况都与右移等价)

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll mp[8][6],vis[10][10],sta[10],del[10][10];
ll top,tt;
const ll n=7,m=5;
struct node{
    ll x,y;
    node(){}
    node(const ll &a,const ll &b){x=a,y=b;}
};
struct an{
    ll x,y,g;
    an(){}
    an(const ll &a,const ll &b,const ll &c){x=a,y=b,g=c;}
}ans[7];
deque<node> q,p;
ll ok(ll x,ll y,ll opt){
    if(x>=1&&x<=n&&y>=1&&y<=m&&mp[x][y]==opt&&!vis[x][y]) return 1; 
    return 0;
}
void print(){
    puts("wwb");
    for(ll i=1;i<=n;i++,puts(""))
        for(ll j=1;j<=m;j++){
            printf("%lld ",mp[i][j]);
        }
}
void down(){
    for(ll i=1;i<=m;i++){
        top=0;
        for(ll j=1;j<=n;j++)
            if(mp[j][i])
                sta[++top]=mp[j][i];
        for(ll j=1;j<=n;j++)
            mp[j][i]=0;
        for(ll j=1;j<=top;j++)
            mp[j][i]=sta[j];
    }
}
void xiao(){
    ll ok=0;
    down();    
    memset(del,0,sizeof(del));
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=m;j++){
            if(mp[i][j]){
                if(i>1&&i<n){
                    if(mp[i][j]==mp[i+1][j]&&mp[i][j]==mp[i-1][j]){
                        del[i][j]=1,del[i+1][j]=1,del[i-1][j]=1;
                        ok=1;
                    }
                }
                if(j>1&&j<m){
                    if(mp[i][j]==mp[i][j-1]&&mp[i][j]==mp[i][j+1]){
                        del[i][j]=1,del[i][j-1]=1,del[i][j+1]=1;
                        ok=1;
                    }
                }
            }
        }
    if(!ok) return ;
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=m;j++)
            if(del[i][j])
                mp[i][j]=0;
    down();
    xiao();
    return ;
}
ll ttt;
ll check(){
    for(ll i=1;i<=m;i++)
        if(mp[1][i]) return 0;
    return 1;
}
void cpy(ll x[][6],ll y[][6]){
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=m;j++)
            x[i][j]=y[i][j];
}
void dfs(ll cnt){
//    printf("%lld
",cnt);
    if(check()&&cnt==tt+1){
        for(ll i=1;i<=tt;i++)
            printf("%lld %lld %lld
",ans[i].x,ans[i].y,ans[i].g);
        exit(0);
    }
    if(cnt>=tt+1) return ;
    ll mem[8][6];
    for(ll j=1;j<=m;j++)
        for(ll i=1;i<=n;i++){
            if(mp[i][j]){                
                if(j!=m&&mp[i][j]!=mp[i][j+1]){
                    cpy(mem,mp);
                    ans[++ttt]=an(j-1,i-1,1);
                    swap(mp[i][j],mp[i][j+1]);
                    xiao();
                    dfs(cnt+1);ttt--;
                    cpy(mp,mem);
                }
                if(j!=1&&mp[i][j]!=mp[i][j-1]){
                    cpy(mem,mp);
                    ans[++ttt]=an(j-1,i-1,-1);
                    swap(mp[i][j],mp[i][j-1]);
                    xiao();
                    dfs(cnt+1);ttt--;
                    cpy(mp,mem);
                }

            }
        }
}
ll cnt[7];
int main(){
    scanf("%lld",&tt);ll x;
    x=1;while(x!=0){scanf("%lld",&x);mp[++cnt[1]][1]=x;}
    x=1;while(x!=0){scanf("%lld",&x);mp[++cnt[2]][2]=x;}
    x=1;while(x!=0){scanf("%lld",&x);mp[++cnt[3]][3]=x;}
    x=1;while(x!=0){scanf("%lld",&x);mp[++cnt[4]][4]=x;}
    x=1;while(x!=0){scanf("%lld",&x);mp[++cnt[5]][5]=x;}
    xiao();
    dfs(1);
    puts("-1");
}
View Code

NOIP2014

联合权值

题解

花5分钟写了写

发现根本不用$Dp$,

$O(n^2)$,max显然就是最大值*次大值,总和思考怎么算

发现就是(sum[i]-w[y])*w[y],sum表示是所有与i相连点权值之和,

$O(n)$式子

显然可以优化,${sum[i]}^2-sumlimits_{y}{w[y]}^2$随便维护一下

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 10007
#define A 1111111
ll tot,n,m,t,ans,mx;
ll sum[A],nxt[A],ver[A],x[A],y[A],head[A],w[A],maxx[A],cimaxx[A],pingf[A];
void add(ll x,ll y){
    nxt[++tot]=head[x],head[x]=tot,ver[tot]=y;
}
int main(){
    scanf("%lld",&n);m=n-1;
    for(ll i=1;i<=m;i++){
        scanf("%lld%lld",&x[i],&y[i]);
        add(x[i],y[i]);add(y[i],x[i]);
    }
    for(ll i=1;i<=n;i++){
        scanf("%lld",&w[i]);
    }
    for(ll i=1;i<=m;i++){
        (sum[x[i]]+=w[y[i]])%=mod,(sum[y[i]]+=w[x[i]])%=mod;
        (pingf[x[i]]+=w[y[i]]*w[y[i]])%=mod;
        (pingf[y[i]]+=w[x[i]]*w[x[i]])%=mod;
        if(w[y[i]]>maxx[x[i]]){
            cimaxx[x[i]]=maxx[x[i]];
            maxx[x[i]]=w[y[i]];
        }
        else if(w[y[i]]>cimaxx[x[i]]){
            cimaxx[x[i]]=w[y[i]];
        }
        if(w[x[i]]>maxx[y[i]]){
            cimaxx[y[i]]=maxx[y[i]];
            maxx[y[i]]=w[x[i]];
        }
        else if(w[x[i]]>cimaxx[y[i]]){
            cimaxx[y[i]]=w[x[i]];
        }
    }
    for(ll i=1;i<=n;i++){
        (ans+=sum[i]*sum[i]%mod-pingf[i])%mod;
        mx=max(cimaxx[i]*maxx[i],mx);
    }
    ans%=mod;
    printf("%lld ",mx);
    printf("%lld
",(ans+mod)%mod);
}
View Code

NOIP2016

蚯蚓

题解

暴力就是开一个堆维护,然后每次取出最大值,维护mark表示标准差可以85分

正解是根据单调性不算标准差

因为每次取出最大值,取出的最大值越来小,另外维护两个队列,维护切出的两部分蚯蚓

剩下两部分蚯蚓也满足单调不升性质

单调队列维护

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
deque<ll> w[4];
ll n,m,q,u,v,t;
ll mark,cnt,mxid,mx;
ll ans[31111111],dl[31111111],o[31111111];
double p;
ll cmp(ll x,ll y){
    return x>y;
}
ll gettop(){
    mx=-123456789101112,mxid=0;
    if(!w[1].empty()){
        if(w[1].front()>mx){
            mx=w[1].front();
            mxid=1;
        }
    }
    if(!w[2].empty()){
        if(w[2].front()>mx){
            mx=w[2].front();
            mxid=2;
        }
    }
    if(!w[3].empty()){
        if(w[3].front()>mx){
            mx=w[3].front();
            mxid=3;
        }
    }
    w[mxid].pop_front();
    return mx;
}
ll wempty(){
    if(w[1].empty()&&w[2].empty()&&w[3].empty()) return 1;
    return 0;
}
int main(){
    scanf("%lld%lld%lld%lld%lld%lld",&n,&m,&q,&u,&v,&t);
    for(ll i=1;i<=n;i++){
        scanf("%lld",&o[i]);
    }
    sort(o+1,o+n+1,cmp);
    for(ll i=1;i<=n;i++){
        w[1].push_back(o[i]);
    }
    
    p=(double)(u)/(double)(v);
    for(ll i=1;i<=m;i++){
        ll x=gettop()+mark;
        double xx=p*x;
        ll x1=floor(p*x),x2=x-x1;
        mark+=q;
        
        w[2].push_back(x1-mark),w[3].push_back(x2-mark);
        ans[i]=x;
    }
    while(!wempty()){
        dl[++dl[0]]=gettop()+mark;
    }
    for(ll i=t;i<=m;i+=t)
        printf("%lld ",ans[i]);
    printf("
");
    for(ll i=t;i<=dl[0];i+=t)
        printf("%lld ",dl[i]);
    printf("
");
}
View Code

NOIP2017

列队

题解

易发现m列有些特殊

前m-1列和m列拆开考虑

开n棵线段树维护n行,在开一棵线段树维护最后一列

每一次操作可简化为

依次进行 1.当前行删除y 2.当前行前移 3.最后列删除x 4.最后列前移

线段树维护查询第k大操作

代码实现有很多技巧,很考验代码能力

代码

//依次进行
//1.当前行删除y
//2.当前行前移
//3.最后列删除x
//4.最后列前移
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 11111111
vector<ll> vec[A];
ll ls[A],rs[A],v[A],toot[A];
ll n,m,tot,cnt,q;
void change(ll &x,ll l,ll r,ll val){
    if(!x) x=++cnt;
    v[x]++;
    if(l==r) return ;
    ll mid=(l+r)>>1;
    if(mid>=val) change(ls[x],l,mid,val);
    else change(rs[x],mid+1,r,val);
}
ll ask(ll &x,ll l,ll r,ll k){
    if(l==r) return l;
    ll mid=(l+r)>>1;
    ll sum=mid-l+1-v[ls[x]];
    if(sum>=k) return ask(ls[x],l,mid,k);
    else return ask(rs[x],mid+1,r,k-sum);
}
ll callast(ll x){
    //查询最后一列
    ll r=ask(toot[n+1],1,tot,x);
    change(toot[n+1],1,tot,r);
    return r<=n?(r-1)*m+m:vec[n+1][r-n-1];
}
ll calcommon(ll x,ll y){
    ll r=ask(toot[x],1,tot,y);
    change(toot[x],1,tot,r);
    return r<m?(x-1)*m+r:vec[x][r-m];
}

int main(){
    ll ret,tmp;
    scanf("%lld%lld%lld",&n,&m,&q);
    tot=max(n,m)+q;
    while(q--){
        ll x,y;
        scanf("%lld%lld",&x,&y);
        if(y==m){
            ret=callast(x);
            vec[n+1].push_back(ret);
            printf("%lld
",ret);
        }
        else{
            ret=calcommon(x,y);
            printf("%lld
",ret);
            vec[n+1].push_back(ret);
            tmp=callast(x);
            vec[x].push_back(tmp);
        }
    }
}
View Code

NOIP2018

货币系统

题解

性质,1.最简系统一定是给定系统子集

2.最简系统任意一种货币不能被其他货币表示

2是显然的

1证明:假设非子集元素为x

若x可以被最简系统表示加入它没用,若不能被表示出,你原本不能表示x,现在加上x可以表示x与最简系统矛盾了

所以可以无脑背包

 

原文地址:https://www.cnblogs.com/znsbc-13/p/11838397.html