2017 清北济南考前刷题Day 5 afternoon

期望得分:100+100+30=230

实际得分:0+0+0=30

 

T1

 

直接模拟

 

#include<cstdio>
#include<iostream>

using namespace std;

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

#define N 1002

int e[N][N],t[N],pos[N];

int ans[N];

int main()
{
    //freopen("rotate.in","r",stdin);
    //freopen("rotate.out","w",stdout);
    int n,p,k,x;
    read(n); read(p); read(k);
    for(int i=1;i<=p;i++) 
    {
        read(e[i][0]); x=e[i][0];
        for(int j=1;j<=x;j++) read(e[i][j]);
    }
    for(int i=1;i<=n;i++) ans[i]=i,pos[i]=i;
    for(int i=p;i;i--)
    {
        x=e[i][0];
        for(int j=1;j<=n;j++) t[j]=pos[j];
        for(int j=1;j<x;j++) ans[pos[e[i][j]]]=e[i][j+1],t[e[i][j+1]]=pos[e[i][j]];
        ans[pos[e[i][x]]]=e[i][1]; t[e[i][1]]=pos[e[i][x]];
        for(int j=1;j<=n;j++) pos[j]=t[j];
        for(int j=1;j<=n;j++) cout<<ans[j]<<' ';
        cout<<'\n';
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
}
View Code

 

T2

当固定区间左端点时,随着右端点的右移,ans 值 减小,or 值增加

所以枚举左端点,二分右端点

st 表查询区间ans、 or 值

#include<cstdio>
#include<cmath>
#include<iostream>

using namespace std;

#define N 100001

const int mod=1e9+7;

int n,a,b,c,d;

int And[N][17],Xor[N][17];

int ans=0;

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

int query(int l,int r,bool ty)
{
    int k=log((double)r-l+1)/log(2.0);
    if(ty) return And[l][k]&And[r-(1<<k)+1][k];
    return Xor[l][k]|Xor[r-(1<<k)+1][k];
}

void pre()
{
    for(int j=1,k=1;j<17;j++,k<<=1)
        for(int i=1;i+k-1<=n;i++)
            And[i][j]=And[i][j-1]&And[i+k][j-1],
            Xor[i][j]=Xor[i][j-1]|Xor[i+k][j-1];
}

void solve()
{
    int l,r,mid,al,ar,xl,xr,res;
    for(int i=1;i<=n;i++)
    {
        l=i;r=n;
        al=ar=xl=xr=-1;
        while(l<=r)
        {
            mid=l+r>>1;
            res=query(i,mid,1);
            if(res<=b) al=mid,r=mid-1;
            else l=mid+1;
        }
        if(al==-1) continue;
        l=al;r=n;
        while(l<=r)
        {
            mid=l+r>>1;
            res=query(i,mid,1);
            if(res>=a) ar=mid,l=mid+1;
            else r=mid-1;
        }
        if(ar==-1) continue;
        l=al;r=ar;
        while(l<=r)
        {
            mid=l+r>>1;
            res=query(i,mid,0);
            if(res>=c) xl=mid,r=mid-1;
            else l=mid+1;
        }
        if(xl==-1) continue;
        l=xl;r=ar;
        while(l<=r)
        {
            mid=l+r>>1;
            res=query(i,mid,0);
            if(res<=d) xr=mid,l=mid+1;
            else r=mid-1;
        }
        if(xr==-1) continue;
        ans+=xr-xl+1;
        ans-=ans>=mod ? mod : 0;
        //printf("%d\n",ans);
    }
    printf("%d",ans);
}

int main()
{
    freopen("range.in","r",stdin);
    freopen("range.out","w",stdout);
    read(n); read(a); read(b); read(c); read(d);
    for(int i=1;i<=n;i++) read(And[i][0]),Xor[i][0]=And[i][0];
    pre();
    solve();
}
View Code

 

考场代码错误1

错误的二分左右端点方法(以and为例):

即每次判断是否同时满足>=a <=b 

这样不满足单调性

while(l<=r)
{
  。。。。。。
  if(res>=a && res<=b) al=mid,r=mid-1;
  else l=mid+1;
}

while(l<=r)
{
  。。。。。。
  if(res>=a && res<=b) ar=mid,l=mid+1;
  else r=mid-1;
}

比如在二分右端点时,算出mid >b 

此时应该右移mid 使结果变小

但在上面代码中不满足 if 条件,mid 左移

正确的二分方式:

while(l<=r)
{
  。。。。。。
  if(res<=b) al=mid,r=mid-1;
  else l=mid+1;
}

while(l<=r)
{
  。。。。。。
  if(res>=a) ar=mid,l=mid+1;
  else r=mid-1;
}

即注意二分的单调性

 

考场代码错误2

st 表 倍增预处理 数组越界

 

T3

 

 

n^3 树形背包

dp[i][j] 表示 以i为根的子树中,捡到k个果子的方案数

枚举x的每个子节点 t

捡:g[j+h]+= dp[x][j]*dp[t][h]

不捡:g[j] += dp[x][j] *   2^(siz[t]-1)

这个t 操作完后,把g赋值给dp[x],即子树t与x合并,清空g 

 

在枚举时加了个 if(!dp[x][j]) continue

所有测试点0.06s 以内跑过

复杂度变了? (我是真不会分析时间复杂度QWQ)

还是不要忽视任何一点儿小优化

#include<cstdio>
#include<cstring>
#include<iostream>

#define N 1001

using namespace std;

const int mod=1e9+7;

int n,k;

int val[N];

int front[N],to[N<<1],nxt[N<<1],tot;

int dp[N][N],g[N],siz[N];

int bit[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
}

void dfs(int x,int fa)
{
    siz[x]=1;
    if(val[x]<=k) dp[x][val[x]]=1;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=fa) dfs(to[i],x),siz[x]+=siz[to[i]];
    for(int i=front[x];i;i=nxt[i])
    {
        int t=to[i];
        if(t==fa) continue;
        memset(g,0,sizeof(g));
        for(int j=0;j<=k;++j)
        {
            if(!dp[x][j]) continue;
            for(int h=0;h<=k;++h)
            {
                if(j+h>k) break;
                if(!dp[t][h]) continue;
                g[j+h]+=1LL*dp[x][j]*dp[t][h]%mod;
                g[j+h]%=mod;
            }
            g[j]+=1LL*dp[x][j]*bit[siz[t]-1]%mod;
            g[j]%=mod;
        }
        for(int j=0;j<=k;++j) dp[x][j]=g[j],g[j]=0;
    }
}

int main()
{
    freopen("fruit.in","r",stdin);
    freopen("fruit.out","w",stdout);
    read(n); read(k);
    bit[0]=1;
    for(int i=1;i<=n;++i) bit[i]=bit[i-1]*2%mod;
    for(int i=1;i<=n;++i) read(val[i]);
    int u,v;
    for(int i=1;i<n;++i) read(u),read(v),add(u,v);
    dfs(1,0);
    cout<<dp[1][k];
} 
View Code

 

标准的n^2

先把父节点的值赋给子节点,再加回来

不是很明白

#include<cstdio>
#include<cstring>
#include<iostream>

#define N 1001

using namespace std;

const int mod=1e9+7;

int n,k;

int val[N];

int front[N],to[N<<1],nxt[N<<1],tot;

int dp[N][N];

int bit[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
}

int dfs(int x,int fa)
{
    int siz=1;
    for(int i=front[x];i;i=nxt[i])
    {
        if(to[i]==fa) continue;
        int t=to[i];
        for(int j=0;j<=k-val[t];++j) dp[t][j+val[t]]=dp[x][j];
        int tmp=dfs(to[i],x);
        for(int j=0;j<=k;++j) dp[x][j]=(dp[t][j]+1LL*dp[x][j]*bit[tmp-1]%mod)%mod;
        siz+=tmp;
    }
    return siz;
}

int main()
{
    freopen("fruit.in","r",stdin);
    freopen("fruit.out","w",stdout);
    read(n); read(k);
    bit[0]=1;
    for(int i=1;i<=n;++i) bit[i]=bit[i-1]*2%mod;
    for(int i=1;i<=n;++i) read(val[i]);
    int u,v;
    for(int i=1;i<n;++i) read(u),read(v),add(u,v);
    //for(int i=1;i<=n;i++) dp[i][val[i]]=1;
    dp[1][val[1]]=1;
    dfs(1,0);
    cout<<dp[1][k];
} 
View Code

 

转载于:https://www.cnblogs.com/TheRoadToTheGold/p/7768498.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值