NOIP 2015 题解

终于搞完了!!!!!
dalao们都说水,但蒟蒻表示挺难的;
有件事很开心;
其中四道题是自己独立AC的;
一道想到正解(Day 2 T3),但不会卡常;
至于还有一道……一点思路也没有(Day 2 T2);

Day 1

T1:

题目:
https://www.luogu.org/problem/show?pid=2615

模拟,日常签到题,注意坐标具有传递性;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int ma[2001][2001],n,tot=1;
void solve()
{
    scanf("%d",&n);
    ma[1][n/2+1]=tot;
    int x=1,y=n/2+1;
    while(tot<n*n)
    {
        tot++;
        if(x==1 && y!=n) x=n,y++;
        else if(x!=1 && y==n) y=1,x--;
        else if(x==1 && y==n) x++;
        else if(x!=1 && y!=n)
        {
            if(!ma[x-1][y+1]) x--,y++;
            else x++;
        }
        ma[x][y]=tot;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++) cout<<ma[i][j]<<" ";
        cout<<endl;
    }
}
int main()
{
    solve();
    return 0;
}

T2:

题目:
https://www.luogu.org/problem/show?pid=2661
tarjan或dfs;

比较裸的求最小环;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
stack<int>s;
const int MAXN=200001;
int dfn[MAXN],low[MAXN],fst[MAXN],nxt[MAXN],scc[MAXN];
int n,tot,tim,cnt,f,ans=324252;
struct hh
{
    int from,to;
}map[MAXN];
void build(int f,int t)
{
    tot++;
    map[tot]=(hh){f,t};
    nxt[tot]=fst[f];
    fst[f]=tot;
    return;
}
void tarjan(int x)
{
    low[x]=dfn[x]=++tim;
    s.push(x);
    for(int i=fst[x];i!=-1;i=nxt[i])
    {
        int v=map[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(!scc[v]) low[x]=min(low[x],dfn[v]);
    }
    if(dfn[x]==low[x])
    {
        cnt++;
        int num=0;
        while(true)
        {
            int u=s.top();
            s.pop();
            num++,scc[x]=cnt;
            if(u==x)
            {
                if(num>1) ans=min(num,ans);
                break;
            }
        }
    }
    return;
}
void solve()
{
    memset(fst,-1,sizeof(fst));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f);
        build(i,f);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i]) tarjan(i);
    cout<<ans;
    return;
}
int main()
{
    solve();
    return 0;
}

T3:
题目:
https://www.luogu.org/problem/show?pid=2668

注意答案的更新;
挺简单的一道题,但是细节处理较多;

题解:http://blog.csdn.net/qq_36312502/article/details/77970178

爆搜;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int t,n,cnt[10001],ans,num;
void init()
{
    memset(cnt,0,sizeof(cnt));
    ans=n;
    return;
}
int cnt1,cnt2,cnt3,cnt4,cnt5;
void dfs(int x)
{
    cnt1=cnt2=cnt3=cnt4=cnt5=0;
    for(int i=1;i<=14;i++)
    {
        if(cnt[i]==1) cnt1++;
        else if(cnt[i]==2) cnt2++;
    }
    for(int i=1;i<=14;i++)//三带一 三带二 
    {
        if(cnt[i]==3)
        {
            cnt3++;
            if(cnt1>=1) cnt1--;
            else if(cnt2>=1) cnt2--;
        }
    }
    for(int i=1;i<=14;i++) //四带二 
    {
        if(cnt[i]==4)
        {
            cnt4++;
            if(cnt1>=2) cnt1-=2;
            else if(cnt2>=2) cnt2-=2;
            else if(cnt2>=1) cnt2--;//四带一个对子也可以;
        }
    }
    ans=min(ans,x+cnt1+cnt2+cnt3+cnt4);
    for(int i=1;i<=8;i++) //单顺子 
    {
        int j;
        for(j=i;j<=12;j++)
        {
            if(cnt[j]<1) break;
            cnt[j]--;
            if(j-i>=4) dfs(x+1);
        }
        for(int k=i;k<=j-1;k++) cnt[k]++;
    }
    for(int i=1;i<=10;i++) // 双顺子 
    {
        int j;
        for(j=i;j<=12;j++)
        {
            if(cnt[j]<2) break;
            cnt[j]-=2;
            if(j-i>=2) dfs(x+1); 
        }
        for(int k=i;k<=j-1;k++) cnt[k]+=2;
    }
    for(int i=1;i<=11;i++) // 三顺子 
    {
        int j;
        for(j=i;j<=12;j++)
        {
            if(cnt[j]<3) break; 
            cnt[j]-=3;
            if(j-i>=1) dfs(x+1); 
        }
        for(int k=i;k<=j-1;k++) cnt[k]+=3;
    }
    return;
}
void solve()
{
    init();
    int x,y;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        if(x==0) cnt[14]++;
        else if(x==1) cnt[12]++;
        else if(x==2) cnt[13]++;
        else cnt[x-2]++;
    }
      dfs(0);
    cout<<ans<<endl;
}
int main()
{
    scanf("%d%d",&t,&n);
    int hh=t;
    while(hh--) solve();
    return 0;
}
Day 2

T1:

题目:
https://www.luogu.org/problem/show?pid=2678

二分;

题解:
http://blog.csdn.net/qq_36312502/article/details/77869900

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int r,l=1,L,n,m,ans,last;
int a[100001];
bool check(int x)
{
    int ans=0,last=0;
    for(int i=1;i<=n+1;i++)
    {
        if(a[i]-last<x) ans++;
        else last=a[i];
    }
    if(ans>m) return 1;
    else return 0;
}
void solve()
{
    cin>>L>>n>>m;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    r=a[n+1]=L;
    while(r-l>1)
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid;
    }
    if(!check(r)) l=r;
    cout<<l;
}
int main()
{
    solve();
    return 0;
}

T2:

题目:
https://www.luogu.org/problem/show?pid=2679

DP,难;
题解:
http://blog.csdn.net/qq_36312502/article/details/78127290

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1000000007;
int dp[3][301][301][3],cnt,now,pre=1,n,m,t;
char s1[1021],s2[1021];
void solve()
{
    cin>>n>>m>>t;
    scanf("%s%s",s1+1,s2+1);
    for(int i=1;i<=n;i++)
    {
        swap(now,pre);
        dp[now][1][1][0]=cnt;
        if(s1[i]==s2[1]) dp[now][1][1][1]=1,cnt++;
        for(int j=2;j<=m;j++)
        {
            for(int k=1;k<=t;k++)
            {
                if(s1[i]==s2[j])
                dp[now][j][k][1]=((dp[pre][j-1][k-1][0]+dp[pre][j-1][k-1][1])%mod+dp[pre][j-1][k][1])%mod;
                dp[now][j][k][0]=(dp[pre][j][k][0]+dp[pre][j][k][1])%mod;
            }
        }
        for(int j=1;j<=m;j++)
            for(int k=1;k<=t;k++) 
                dp[pre][j][k][1]=dp[pre][j][k][0]=0;
    }
    cout<<(dp[now][m][t][1]+dp[now][m][t][0])%mod;
    return;
}
int main()
{
    solve();
    return 0;
}

T3:

题目:
https://www.luogu.org/problem/show?pid=2680

lca+树上差分+二分;

题解:
http://blog.csdn.net/qq_36312502/article/details/78121945

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN=600008;
int fst[MAXN],nxt[MAXN],num[MAXN],tmp[MAXN],dis[MAXN];
int deep[MAXN],top[MAXN],sz[MAXN],fa[MAXN],son[MAXN],dfn[MAXN];
int tot,n,m,l=-1,r,totp;
struct hh {int from,to,cost;}ss[MAXN];
struct edge {int from,to,lca,dis;}ma[MAXN];
void build(int f,int t,int c)
{
    tot++;
    ss[tot]=(hh){f,t,c};
    nxt[tot]=fst[f];
    fst[f]=tot;
    return;
}
void dfs1(int x,int f,int c)
{
    dfn[++totp]=x;
    fa[x]=f,deep[x]=deep[f]+1,sz[x]=1,dis[x]=c;
    for(int i=fst[x];i;i=nxt[i])
    {
        int v=ss[i].to;
        if(v==f) continue;
        num[v]=ss[i].cost;
        dfs1(v,x,c+ss[i].cost),sz[x]+=sz[v];
        if(!son[x] || sz[son[x]]<sz[v]) son[x]=v;
    }
    return;
}
void dfs2(int x,int st)
{
    top[x]=st;
    if(!son[x]) return;
    dfs2(son[x],st);
    for(int i=fst[x];i;i=nxt[i])
    {
        int v=ss[i].to;
        if(v==fa[x] || v==son[x]) continue;
        dfs2(v,v);
    }
    return;
}
int lca(int x,int y)
{
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(deep[fx]<deep[fy]) swap(fx,fy),swap(x,y);
        x=fa[fx],fx=top[x];
    }
    return deep[x]<deep[y]?x:y;
}
bool check(int ans)
{
    int lim=-1,cnt=0;
    memset(tmp,0,sizeof(tmp));
    for(int i=1;i<=m;i++)
    {
        if(ma[i].dis>ans)
        {
            tmp[ma[i].from]++,tmp[ma[i].to]++;
            tmp[ma[i].lca]-=2;
            lim=max(lim,ma[i].dis-ans);
            cnt++;
        }
    }
    if(!cnt) return true;
    for(int i=n;i>=1;i--) tmp[fa[dfn[i]]]+=tmp[dfn[i]];
    for(int i=2;i<=n;i++)
        if(num[i]>=lim && tmp[i]==cnt) return true;
    return false;
}
void solve()
{
    int x,y,z;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        build(x,y,z),build(y,x,z);
        r+=z;
    }
    dfs1(1,0,0),dfs2(1,1);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        ma[i]=(edge){x,y,lca(x,y),dis[x]+dis[y]-2*dis[lca(x,y)]};
    }
    while(r-l>1)
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid;
    }
    cout<<r;
    return;
}
int main()
{
    solve();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值