noip模拟题 小奇2 by hzwer[DP][路径压缩][分类讨论][位运算]

这么颓下去,迟早要完。

这套题只考了2h,感觉还不错,T2做过类似的所以A了,T1和T3基本上也没什么大问题,关键就是要深入挖掘问题特质,学会分类讨论(很多题都可以剪掉大量的枝,比如T1,小奇1的T3,noip2015 day1 T3,都是需要有这种分类意识的。
T1:
题意:在坐标轴上有一些点有收益,问从0开始,每次只能向前跳4(0+4=4)步或7(0+7=7)步,最大收益是。
数据范围:
对于20%的数据n=1,m<=10^5
对于40%的数据n<=15,m<=10^5
对于60%的数据m<=10^5
对于100%的数据n<=10^5,m<=10^9,1<=ai<=10^4,1<=bi<=m
分析:
感觉这道题是数据分斥(还是什么,不知道叫什么)的典型例子,黄学长这两套题出的还是不错的。
20%n=1,只用看能不能去就行了。
60%的数据,m比较小,可以f[i]表示i这个位置的最大值,那么然后去找-4,-7的最大值就可以了。
100%,经过分析发现,>=18的情况是一定可以走到的!那么就可以18以内用数组存能不能走,18以外都可以走,在走的时候更新18以内的最大值就可以了!
下面是碎碎念可以不看:
关键就是发现>=18的那个性质,这个要求我们多手算找规律啊。
按理说发现>=18以后一百分算法应该顺理成章不过我居然没发现,还是没养成这种意识。
而且考这套题的时候太zz了,因为我的算法应该是40分,我想了很久后面那20分怎么拿,结果拿到解题报告才想起来,枚举m的话时间复杂度线性……

60分程序:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define long long ll
#define inf 2e8
#define modd 1e9+7
#define clr(x) memset(x,0,sizeof(x))
#define maxen(x) memset(x,127,sizeof(x))
#define maxer(x) memset(x,31,sizeof(x))
#define each(i,n) for(int i=1;i<=n;i++)
#define minn(a,b,c) min(a,min(b,c))
#define maxx(a,b,c) max(a,max(b,c))
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define PROC "mining"
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
const int Maxn=1e5+5;
int n,m,ans,f[Maxn];
struct node
{
    int a,b;
}a[Maxn];
bool cmp(node x,node y)
{
    return x.aint read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void init()
{
    n=read();m=read();
    each(i,n){
        a[i].b=read();
        a[i].a=read();
    }
    sort(a+1,a+n+1,cmp);
}
int boo[20]={1,0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,1};
int check(int cur,int pre)
{
    if(cur-pre>=18)return 1;
    return boo[cur-pre];
}
void work()
{
    for(int i=1;i<=n;i++){
        int j=i-1;int flg=1;
        for(;j>=0;j--)if(check(a[i].a,a[j].a))
        f[i]=max(f[j]+a[i].b,f[i]);
        ans=max(ans,f[i]);
    }
    printf("%d",ans);
}
void debug()
{
    //
}
int main()
{
    freopen("hop.in","r",stdin);
    freopen("hop.out","w",stdout);
    init();
    work();
    //debug();
    return 0;
}

100分:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define long long ll
#define inf 2e8
#define modd 1e9+7
#define clr(x) memset(x,0,sizeof(x))
#define maxen(x) memset(x,127,sizeof(x))
#define maxer(x) memset(x,31,sizeof(x))
#define each(i,n) for(int i=1;i<=n;i++)
#define minn(a,b,c) min(a,min(b,c))
#define maxx(a,b,c) max(a,max(b,c))
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define PROC "mining"
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
const int Maxn=1e5+5;
int n,m,ans,maxn,f[Maxn];
struct node
{
    int a,b;
}a[Maxn];
bool cmp(node x,node y)
{
    return x.aint read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void init()
{
    n=read();m=read();
    each(i,n){
        a[i].b=read();
        a[i].a=read();
    }
    sort(a+1,a+n+1,cmp);
}
int boo[20]={1,0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,1};
int check(int cur,int pre)
{
    if(cur-pre>=18)return 1;
    return boo[cur-pre];
}
void work()
{
    for(int i=1;i<=n;i++){
        int j=i-1;
        for(;j>=max(0,i-17);j--)if(check(a[i].a,a[j].a))f[i]=max(f[j]+a[i].b,f[i]);
        if(i>18)maxn=max(maxn,f[i-18]);
        if(check(a[i].a,0))f[i]=max(f[i],maxn+a[i].b);
        ans=max(ans,f[i]);
    }
    printf("%d",ans);
}
void debug()
{
    //
}
int main()
{
    freopen("hop.in","r",stdin);
    freopen("hop.out","w",stdout);
    init();
    work();
    //debug();
    return 0;
}

T2:
题意:从带权矩阵左上角走到右下角(只能向下或向右走),求 (n+m1)Σn+m1i=1(AiAavg)2 的最小值.
分析:首先呢我们可以证明对于一个序列,若平均值错误,V值一定更小(具体怎么证?考虑一个长度为2的序列,把它拆开,看一下,这不是均值不等式吗)。
然后,你看这权值范围<=30,一看就可以做奇怪的事。根据最小精度枚举平均值情况就可以了。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define long long ll
#define inf 2e8
#define modd 1e9+7
#define clr(x) memset(x,0,sizeof(x))
#define maxen(x) memset(x,127,sizeof(x))
#define maxer(x) memset(x,31,sizeof(x))
#define each(i,n) for(int i=1,i<=n;i++)
#define minn(a,b,c) min(a,min(b,c))
#define maxx(a,b,c) max(a,max(b,c))
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define PROC ""
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
const int Maxn=305;
int t,n,m,lim,limi,ans,a[Maxn][Maxn],f[Maxn][Maxn];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void init()
{
   n=read();m=read();lim=n+m-1;
   for(int i=2;i<=n;i++)f[i][0]=inf;
   for(int j=2;j<=m;j++)f[0][j]=inf;
   for(int i=1;i<=n;i++)
       for(int j=1;j<=m;j++)
           a[i][j]=read()*lim;
   limi=lim*30;ans=2e9;
}
void work()
{
    for(int i=lim;i<=limi;i++){
        for(int j=1;j<=n;j++)
        for(int k=1;k<=m;k++)
            f[j][k]=min(f[j][k-1],f[j-1][k])+(a[j][k]-i)*(a[j][k]-i);
        ans=min(ans,f[n][m]);
    }
    printf("%d\n",ans/lim);
}
void debug()
{
    //
}
int main()
{
    freopen("matrix.in","r",stdin);
    freopen("matrix.out","w",stdout);
    t=read();
    for(int i=1;i<=t;i++){
    init();
    work();
    }
    //debug();
    return 0;
}

T3:
题意:对于一棵树,对于每个点,求其他点到这个点的距离分别异或一值的和。
分析:
其实吧,多法图你就会发现,这个东西,其实可以通过统计最后几位的01来解决。具体的实现就是,用处理0那种情况(也就是两次dfs记两个数组)的方法,再同时处理个01就可以了,然而我当初太天真,写了个暴力。
碎碎念:多fa图。
暴力:(好像m!=0处理有错WA了一组,求大神指点。)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define inf 2e8
#define modd 1e9+7
#define clr(x) memset(x,0,sizeof(x))
#define maxen(x) memset(x,127,sizeof(x))
#define maxer(x) memset(x,31,sizeof(x))
#define each(i,n) for(int i=1;i<=n;i++)
#define minn(a,b,c) min(a,min(b,c))
#define maxx(a,b,c) max(a,max(b,c))
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define PROC ""
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
const int Maxn=1e5+5;
int n,m,a,b,ans,roa[Maxn],g[Maxn],son[Maxn];
int f[Maxn],fr[Maxn],tov[2*Maxn],des[2*Maxn],wor[2*Maxn];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void addedge(int cur)
{
    a=read();b=read();wor[2*cur]=wor[2*cur-1]=read();
    tov[2*cur-1]=fr[a];fr[a]=2*cur-1;des[2*cur-1]=b;
    tov[2*cur]=fr[b];fr[b]=2*cur;des[2*cur]=a;
}
void dfs(int u,int fath,long long dis)
{
    if(dis)ans+=dis^m;
    for(int i=fr[u];i;i=tov[i])
        if(des[i]!=fath)
        dfs(des[i],u,dis+(long long)wor[i]);
}
void dfs2(int u,int fath)
{
    for(int i=fr[u];i;i=tov[i])
    if(des[i]!=fath){
        dfs2(des[i],u);
        roa[des[i]]=wor[i];son[u]+=1+son[des[i]];
        f[u]+=f[des[i]]+wor[i]*(son[des[i]]+1);
    }
}
void dfs3(int u,int fath)
{
    g[u]=f[fath]-f[u]+g[fath]+roa[u]*(n-son[u]*2-2);
    for(int i=fr[u];i;i=tov[i])
    if(des[i]!=fath)
        dfs3(des[i],u);
}
void init()
{
   n=read();m=read();
   for(int i=1;ivoid work()
{
    if(m)
    {
        for(int i=1;i<=n;i++){
            ans=0;dfs(i,0,0LL);
            printf("%d\n",ans);
        }
    }
    else{
        dfs2(1,0);
        f[0]=f[1];dfs3(1,0);
        for(int i=1;i<=n;i++)
            printf("%d\n",f[i]+g[i]);
    }
}
void debug()
{
    //
}
int main()
{
    freopen("tree1.in","r",stdin);
    freopen("tree1.out","w",stdout);
    init();
    work();
    //debug();
    return 0;
}

你可能感兴趣的:(模拟题,DP,路径压缩,分类讨论,位运算,noip,noip模拟题,位运算,DP,路径压缩)