noip2015

NOIP 2015

Day1

T1 P2615 [NOIP2015 提高组] 神奇的幻方

解题方法在题面里都告诉了,模拟就完事。

#include<iostream>
using namespace std;
const int mm=40;
int m[mm][mm];
int i,j,n,k;
int main()
{
    cin>>n;
    for(i=0; i<=n+1; i++)
    {
        for(j=0; j<=n+1; j++)
        {
            if(i>0&&i<=n&&j>0&&j<=n)
                m[i][j]==0;
            else
                m[i][j]==1;
        }
    }
    m[1][n/2+1]=1;
    i=1;
    j=n/2+1;
    k=2;

    while(k<=n*n)
    {
        if(i==1&&j!=n)
        {
            m[n][j+1]=k;
            i=n;
            j=j+1;
            k++;
            continue;
        }
        else if(i!=1&&j==n)
        {
            m[i-1][1]=k;
            j=1;
            i=i-1;
            k++;
            continue;
        }
        else if(i==1&&j==n)
        {
            m[i+1][j]=k;
            i=i+1;
            k++;
            continue;
        }
        else if(i!=1&&j!=n)
        {
            //cout<<"emm";
            if(m[i-1][j+1]==0)
            {
                m[i-1][j+1]=k;
                i-=1;
                j+=1;
                k++;
                continue;
            }
            else
            {
                m[i+1][j]=k;
                i+=1;
                k++;
                continue;
            }
        }

        /*
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                cout<<m[i][j];
            }
            cout<<endl;
        }

        cout<<k<<' '<<endl;*/
    }

    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            cout<<m[i][j]<<' ';
        }
        cout<<endl;
    }

    return 0;
}
View Code

T2 P2661 [NOIP2015 提高组] 信息传递

一道求最小环的问题,且每个点只有一个出边,可以用带权并查集解决(并非通法)。

#include<bits/stdc++.h>
using namespace std;
int ans,n,t[200001],f[200001],s[200001];
int fa(int a)
{
    if(f[a]!=a)
    {
        int ff=f[a];
        f[a]=fa(f[a]);
        s[a]+=s[ff];
    }
    return f[a];
}
void cir(int a,int b)
{
    int x=fa(a),y=fa(b);
    if(x==y)
        ans=min(ans,s[a]+s[b]+1);
    else
        f[x]=y,s[a]=s[b]+1;
}
int main()
{
    ans=9999999;
    cin>>n;
    for(int i=1;i<=n;i++)
        f[i]=i;
    for(int i=1;i<=n;i++)
    {
        cin>>t[i];
        cir(i,t[i]);
    }
    cout<<ans;
    return 0;
}
View Code

拓扑排序求最小环

T3 P2668 [NOIP2015 提高组] 斗地主

此处省略一万行模拟,你懂的。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define mxh 1000000007
using namespace std;
int ans,T,n,i,x,y,pai[6],cnt[15];
void find(int step){
    int i,j,k,w;
    if(step>=ans)return;
    ans=min(ans,step+pai[1]+pai[2]+pai[3]+pai[4]);
    if(pai[4])for(i=2;i<=14;i++)if(cnt[i]==4){
        pai[4]--;cnt[i]-=4;
        if(!pai[3]&&!pai[4]&&!pai[1]&&pai[2]<=1){
            ans=min(ans,step+1);
            pai[4]++;cnt[i]+=4;
            return;
        }
        for(j=1;j<=14;j++)if(cnt[j]){
            pai[cnt[j]]--;cnt[j]--;pai[cnt[j]]++;
            for(k=j;k<=14;k++)if(cnt[k]){
                pai[cnt[k]]--;cnt[k]--;pai[cnt[k]]++;
                find(step+1);
                pai[cnt[k]]--;cnt[k]++;pai[cnt[k]]++;
            }
            pai[cnt[j]]--;cnt[j]++;pai[cnt[j]]++;
        }
        if(pai[2]||pai[3]||pai[4])for(j=2;j<=14;j++)if(cnt[j]>1){
            pai[cnt[j]]--;cnt[j]-=2;pai[cnt[j]]++;
            for(k=j;k<=14;k++)if(cnt[k]>1){
                pai[cnt[k]]--;cnt[k]-=2;pai[cnt[k]]++;
                find(step+1);
                pai[cnt[k]]--;cnt[k]+=2;pai[cnt[k]]++;
            }
            pai[cnt[j]]--;cnt[j]+=2;pai[cnt[j]]++;
        }
        pai[4]++;cnt[i]+=4;
    }
    if(pai[3])for(i=2;i<=14;i++)if(cnt[i]>=3){
        pai[cnt[i]]--;cnt[i]-=3;pai[cnt[i]]++;
        find(step+1);
        for(j=1;j<=14;j++)if(cnt[j]){
            pai[cnt[j]]--;cnt[j]--;pai[cnt[j]]++;
            find(step+1);
            pai[cnt[j]]--;cnt[j]++;pai[cnt[j]]++;
        }
        if(pai[2]||pai[3]||pai[4])for(j=2;j<=14;j++)if(cnt[j]>1){
            pai[cnt[j]]--;cnt[j]-=2;pai[cnt[j]]++;
            find(step+1);
            pai[cnt[j]]--;cnt[j]+=2;pai[cnt[j]]++;
        }
        pai[cnt[i]]--;cnt[i]+=3;pai[cnt[i]]++;
    }
    if(pai[3]+pai[4]>=2)for(i=3;i<14;i++)if(cnt[i]>=3&&cnt[i+1]>=3){
        pai[cnt[i]]--;cnt[i]-=3;pai[cnt[i]]++;w=i;
        for(j=i+1;cnt[j]>=3&&j<=14;j++){
            pai[cnt[j]]--;cnt[j]-=3;pai[cnt[j]]++;
            find(step+1);w=j;
        }
        for(j=i;j<=w;j++){
            pai[cnt[j]]--;cnt[j]+=3;pai[cnt[j]]++;
        }
    }
    if(pai[2]+pai[3]+pai[4]>=3)for(i=3;i<13;i++)if(cnt[i]>=2&&cnt[i+1]>=2&&cnt[i+2]>=2){
        pai[cnt[i]]--;cnt[i]-=2;pai[cnt[i]]++;
        pai[cnt[i+1]]--;cnt[i+1]-=2;pai[cnt[i+1]]++;w=i+1;
        for(j=i+2;cnt[j]>=2&&j<=14;j++){
            pai[cnt[j]]--;cnt[j]-=2;pai[cnt[j]]++;
            find(step+1);w=j;
        }
        for(j=i;j<=w;j++){
            pai[cnt[j]]--;cnt[j]+=2;pai[cnt[j]]++;
        }
    }
    if(pai[1]+pai[2]+pai[3]+pai[4]>=5)for(i=3;i<11;i++)if(cnt[i]&&cnt[i+1]&&cnt[i+2]&&cnt[i+3]&&cnt[i+4]){
        pai[cnt[i]]--;cnt[i]--;pai[cnt[i]]++;
        pai[cnt[i+1]]--;cnt[i+1]--;pai[cnt[i+1]]++;
        pai[cnt[i+2]]--;cnt[i+2]--;pai[cnt[i+2]]++;
        pai[cnt[i+3]]--;cnt[i+3]--;pai[cnt[i+3]]++;w=i+3;
        for(j=i+4;cnt[j]&&j<=14;j++){
            pai[cnt[j]]--;cnt[j]--;pai[cnt[j]]++;
            find(step+1);w=j;
        }
        for(j=i;j<=w;j++){
            pai[cnt[j]]--;cnt[j]++;pai[cnt[j]]++;
        }
    }
}
int main(){
    for(scanf("%d%d",&T,&n);T--;){
        ans=12;
        memset(cnt,0,sizeof(cnt));
        memset(pai,0,sizeof(pai));
        for(i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            if(x==1)x=14;
            if(x==0)x=1;
            cnt[x]++;
        }
        for(i=1;i<=14;i++)pai[cnt[i]]++;
        find(0);
        printf("%d\n",ans);
    }
}
View Code

Day2

T1 P2678 [NOIP2015 提高组] 跳石头

显然,我们当前所在的石头越靠前,越容易拉大距离,所以去掉石头直到距离下块石头足够远,然后就跳到下一块,这样贪心便可得到能否使最短距离达到当前的mid,二分答案的前提成立。

#include<bits/stdc++.h>
using namespace std;
long L,n,m,a[50010];
bool check(int k)
{
    long rem=0,now=0;
    for(int i=1;i<=n+1;i++)
    {
        if((a[i]-a[now])<k)
            rem++;
        else
            now=i;
    }
    if(rem>m)
            return 0;
    return 1;
}
int main()
{
    //freopen("2678.in","r",stdin);
    cin>>L>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    a[n+1]=L;
    long l=0,r=1000000001,m;
    while(r-l>1)
    {
        m=(l+r)/2;
        if(check(m))
            l=m;
        else
            r=m;
    }
    cout<<l;
    return 0;
}
View Code

T2 P2679 [NOIP2015 提高组] 子串

dp。

思路,显然至少要两维表示a串和b串的前i,j位,然后又要一维k记录分几段。f[i][j][k]表示a串前i位分成k段构成b的方案数。

考虑转移,结果发现无法转移新的点是否与上一块相连,所以再加一维表示最后一点是否被使用。

转移:

f[i][j][k][0]=f[i-1][j-1][k][0]+f[i-1][j-1][k][1]

若 a[i]==b[i] f[i][j][k][1]=f[i-1][j-1][k][1]+f[i-1][j-1][k-1][0]+f[i-1][j-1][k-1][1]

初始化:

枚举a[i=1~n] a[i]=b[1] f[a][1][1][1]=1

#include<bits/stdc++.h>
using namespace std;
int mod=1000000007;
int n,m,t,s,now,pre;
int f[2][201][201][2];
string s1,s2;
int main()
{
    cin>>n>>m>>t;
    cin>>s1>>s2;s1=' '+s1;s2=' '+s2;
    now=0,pre=1;
    for(int i=1;i<=n;i++)
    {
        swap(now,pre);
        f[now][1][1][0]=s;
        if(s1[i]==s2[1])
            f[now][1][1][1]=1,s++;
        for(int j=2;j<=m;j++)
        {
            for(int k=1;k<=t;k++)
            {
                if(s1[i]==s2[j])
                    f[now][j][k][1]=((f[pre][j-1][k-1][1]+f[pre][j-1][k][1])%mod+f[pre][j-1][k-1][0])%mod;
                f[now][j][k][0]=(f[pre][j][k][0]+f[pre][j][k][1])%mod;
            }    
        }
        for(int j=1;j<=m;j++)
            for(int k=1;k<=t;k++)
                f[pre][j][k][1]=f[pre][j][k][0]=0;
    }
    cout<<(f[now][m][t][1]+f[now][m][t][0])%mod;
    return 0;
}
View Code

T3 P2680 [NOIP2015 提高组] 运输计划

二分+求链长+贪心

首先答案是符合单调性的所以首先想到二分答案,其实想到二分答案这道题就已经解决一半了。有二分就有check函数,首先求出每条链长(lca,树链剖分)找所有长度大于mid的,用差分求出被所有链都经过的权值最大的边,若不存在这样的边就返回false,存在则返回最长链-该边权值是否大于mid。

#include<bits/stdc++.h>
using namespace std;
const int MM=600005;
int ccnt,nnum[MM],cnt,tot,_max,x,b,u,v,w,n,m,nxt[MM],cf[MM],head[MM],to[MM],dis[MM],dep[MM],f[MM][26],d[MM][26],D[MM];
int l,r,mid,s[MM],t[MM];
void add(int u,int v,int w)
{
    nxt[++tot]=head[u];
    head[u]=tot;
    to[tot]=v;
    dis[tot]=w;
}
void dfs(int now,int fat,int deep)
{
    ccnt++;
    nnum[ccnt]=now;
    dep[now]=deep;
    f[now][0]=fat;
    for(int i=head[now];i;i=nxt[i])
    {
        if(to[i]==fat)
        {
            d[now][0]=dis[i];
            continue;
        }
        dfs(to[i],now,deep+1);
    }
}
void build(int now)
{
    for(int i=1;i<=25;i++)
        d[now][i]=d[now][i-1]+d[f[now][i-1]][i-1],f[now][i]=f[f[now][i-1]][i-1];
    for(int i=head[now];i;i=nxt[i])
        if(to[i]!=f[now][0])
            build(to[i]);
}
int getlca(int a,int b)
{
    if(dep[b]>dep[a])
        swap(a,b);
    for(int i=25;i>=0;i--)
        if(f[a][i]&&dep[f[a][i]]>=dep[b])
            a=f[a][i];
    if(a==b)
        return a;
    for(int i=25;i>=0;i--)
        if(f[a][i]!=f[b][i]&&f[a][i]&&f[b][i]) 
            a=f[a][i],b=f[b][i];
    return f[a][0];
}
int getdis(int a,int b)
{
    int tmp=0;
    if(dep[b]>dep[a])
        swap(a,b);
    for(int i=25;i>=0;i--)
        if(f[a][i]&&dep[f[a][i]]>=dep[b])
            tmp+=d[a][i],a=f[a][i];
    if(a==b)
        return tmp;
    for(int i=25;i>=0;i--)
        if(f[a][i]!=f[b][i]&&f[a][i]&&f[b][i]) 
            tmp+=d[a][i],tmp+=d[b][i],a=f[a][i],b=f[b][i];
    tmp+=d[a][0]+d[b][0]; 
    return tmp;
}
void sum(int now,int x)
{
    for(int i=head[now];i;i=nxt[i])
        if(to[i]!=f[now][0])
            sum(to[i],x),cf[now]+=cf[to[i]];
    if(cf[now]==cnt&&d[now][0]>=_max-x)
        b=1;
}
bool check(int x)
{
    cnt=0;b=0;
    memset(cf,0,sizeof(cf));
    for(int i=1;i<=m;i++)
        if(D[i]>x)
            cf[s[i]]++,cf[t[i]]++,cf[getlca(s[i],t[i])]-=2,cnt++;
    if(cnt==0)
        return 1;
    for(int i=n;i>=1;i--)
        cf[f[nnum[i]][0]]+=cf[nnum[i]];
    for(int i=2;i<=n;i++)
        if(cf[i]==cnt&&d[i][0]>=_max-x)
            return 1;
    return 0;
} 
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n-1;i++)
    {
        cin>>u>>v>>w;
        add(u,v,w);    
        add(v,u,w);
    }
    dfs(1,0,1);
    build(1); 
    for(int i=1;i<=m;i++)
        cin>>s[i]>>t[i],D[i]=getdis(s[i],t[i]),_max=max(D[i],_max);
    l=0,r=_max+100;
    while(r>l)
    {
        mid=(l+r)>>1;
        if(check(mid))
            r=mid;
        else l=mid+1;
    }
    cout<<l;
    return 0;
}
View Code

 

 

posted @ 2021-08-17 18:27  T_X蒻  阅读(47)  评论(0编辑  收藏  举报