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]<<‘ ‘;
}

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();
}

考场代码错误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];
} 

标准的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];
} 

时间: 2024-10-13 09:50:10

2017 清北济南考前刷题Day 5 afternoon的相关文章

2017 清北济南考前刷题Day 1 morning

期望得分:100+100+50=250 实际得分:100+60+50=210 T2 二分 估错上界.估错复杂度 T1 立方数(cubic) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK定义了一个数叫“立方数”,若一个数可以被写作是一个正整数的3次方,则这个数就是立方数,例如1,8,27就是最小的3个立方数. 现在给定一个数P,LYK想要知道这个数是不是立方数. 当然你有可能随机输出一些莫名其妙的东西来骗分,因此LYK有T次询问~ 输入格式(cub

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

期望得分:100+100+0=200 实际得分: 坐标的每一位不是0就是1,所以答案就是 C(n,k) #include<cstdio> #include<iostream> using namespace std; const int mod=1e9+7; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0';

2017 清北济南考前刷题Day 3 morning

实际得分:100+0+0=100 T1 右上角是必败态,然后推下去 发现同行全是必胜态或全是必败态,不同行必胜必败交叉 列同行 所以n,m 只要有一个是偶数,先手必胜 #include<cstdio> using namespace std; int main() { freopen("star.in","r",stdin); freopen("star.out","w",stdout); int n,m; whi

2017 清北济南考前刷题Day 2 morning

期望得分:100+30+60=190 实际得分:100+30+30=160 T1 最优方案跳的高度一定是单调的 所以先按高度排序 dp[i][j] 跳了i次跳到j 枚举从哪儿跳到j转移即可 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 51 struct node { int c,h; }e[N]

清北考前刷题day7早安

清北考前刷题da7下午好

三向城 /* 原图一定是一棵完全二叉树. 根节点是x,左节点是x*2,右节点是x*2+1 转化为二进制往左右走就很明显了. */ #include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; int T,x,y,k,ans,pos; inline int read() { int x=0,f=1;char c=getchar(); while

清北考前刷题day6下午好

/* 贪心 负数一定不取 枚举最高位是1 且答案取为0的 位置, 更新答案. */ #include<iostream> #include<cstdio> #include<cstring> #define ll long long #define N 100010 using namespace std; int n; ll a[N],ans,sum[N]; char s[N]; ll read() { ll x=0,f=1;char c=getchar(); whi

2017清北精英班整理内容掌握考试题

精英班考试题 2017.2.10 题目名 工资 藏妹子之处 银河之星 源文件 money.cpp/c/pas excel.pas/cpp galaxy.cpp/c/pas 输入文件 money.in excel.in galaxy.in 输出文件 money.out excel.out galaxy.out 时间限制 1000MS 1000MS 1000MS 内存限制 256MB 128MB 256MB 测试点 10 10 10 测试点分值 10 10 10 第一题 工资 链接 第二题  藏妹子

2017清北学堂集训笔记——图论

我们进入一个新的模块——图论! emmmmm这个专题更出来可能有点慢别介意,原因是要划的图和要给代码加的注释比较多,更重要的就是...这几个晚上我在追剧!!我们的少年时代超级超级超级好看,剧情很燃啊!!咳咳,好吧下面回归正题. 一.图的存储: 1.邻接矩阵: 假设有n个节点,建立一个n×n的矩阵,第i号节点能到达第j号节点就将[i][j]标记为1(有权值标记为权值), 样例如下图: 1 /*无向图,无权值*/ 2 int a[MAXN][MAXN];//邻接矩阵 3 int x,y;//两座城市