HNOI的背景,只不过题目不一样

T1队长快跑

不难看出一个n方dp,dp[ i ][ j ]表示处理到 i 的时候最小的a为j的情况,

然后分情况进行讨论就好了,

发现处理第i层的时候首先要继承前一层的状态,然后再判断是否要摧毁,那么这个题我们可以用线段树优化一下查询和修改的操作

复杂度可以降低到nlog(n);

noip模拟14_i++noip模拟14_i++_02
  1 #include<bits/stdc++.h>
  2 #define lid id<<1
  3 #define rid id<<1|1
  4 using namespace std;
  5 inline int read()
  6 {
  7     int x=0,f=1;
  8     char ch=getchar();
  9     while(ch<'0'||ch>'9')
 10     {
 11         if(ch=='-')
 12         f=-1;
 13         ch=getchar();
 14     }
 15     while(ch>='0'&&ch<='9')
 16     {
 17         x=(x<<1)+(x<<3)+(ch^48);
 18         ch=getchar();
 19     }
 20     return x*f;
 21 }
 22 const int maxn=1e5+1;
 23 int n,tot;
 24 int a[maxn],b[maxn],maxx,maxxx,len;
 25 int c[maxn*2];
 26 int dp[2000][2000];
 27 struct seg_tree{
 28     int l,r,lazy,mx;
 29 }tr[maxn<<4];
 30 void pushup(int id)
 31 {
 32     tr[id].mx=max(tr[lid].mx,tr[rid].mx);
 33 }
 34 void pushdown(int id)
 35 {
 36     if(!tr[id].lazy)
 37     return ;
 38     tr[lid].lazy+=tr[id].lazy;
 39     tr[lid].mx+=tr[id].lazy;
 40     tr[rid].lazy+=tr[id].lazy;
 41     tr[rid].mx+=tr[id].lazy;
 42     tr[id].lazy=0;
 43 }
 44 void build(int id,int l,int r)
 45 {
 46     tr[id].l=l;
 47     tr[id].r=r;
 48     if(l==r)
 49     {
 50         tr[id].mx=0;
 51         return;
 52     }
 53     int mid=(l+r)>>1;
 54     build(lid,l,mid);
 55     build(rid,mid+1,r);
 56     pushup(id);
 57 }
 58 void update(int id,int l,int r,int val)
 59 {
 60     if(tr[id].l>=l&&tr[id].r<=r)
 61     {
 62         tr[id].mx+=val;
 63         tr[id].lazy+=val;
 64         return ;
 65     }
 66     pushdown(id);
 67     int mid=(tr[id].l+tr[id].r)>>1;
 68     if(l<=mid)
 69     update(lid,l,r,val);
 70     if(r>mid)
 71     update(rid,l,r,val);
 72     pushup(id);
 73 }
 74 void modify(int id,int pos,int val)
 75 {
 76     if(tr[id].l==tr[id].r)
 77     {
 78         tr[id].mx=max(tr[id].mx,val);
 79         return;
 80     }
 81     int mid=(tr[id].l+tr[id].r)>>1;
 82     pushdown(id);
 83     if(pos<=mid)
 84     modify(lid,pos,val);
 85     if(pos>mid)
 86     modify(rid,pos,val);
 87     pushup(id);
 88 }
 89 inline int query(int id,int l,int r)
 90 {
 91     if(tr[id].l>=l&&tr[id].r<=r)
 92     return tr[id].mx;
 93     pushdown(id);
 94     int mid=(tr[id].l+tr[id].r)>>1;
 95     int ans=0;
 96     if(l<=mid)
 97     ans=max(query(lid,l,r),ans);
 98     if(r>mid)
 99     ans=max(query(rid,l,r),ans);
100     return ans;
101 }
102 int main()
103 {
104     //FILE*A=freopen("a.in","r",stdin);差点忘记关了
105     n=read();
106     for(int i=1;i<=n;i++)
107     {
108         a[i]=read(),b[i]=read();
109         c[++tot]=a[i],c[++tot]=b[i];
110     }
111     sort(c+1,c+tot+1);
112     len=unique(c+1,c+tot+1)-c-1;
113     for(int i=1;i<=n;i++)
114     {
115         a[i]=lower_bound(c+1,c+len+1,a[i])-c;
116         b[i]=lower_bound(c+1,c+len+1,b[i])-c;
117     }
118     /*for(int i=1;i<=n;i++)
119     {
120         if(a[i]<=b[i])
121         {
122             maxx=0;
123             for(int j=1;j<=len;j++)
124             dp[i][j]=dp[i-1][j];
125             for(int j=b[i]+1;j<=len;j++)
126             maxx=max(maxx,dp[i][j]);
127             dp[i][a[i]]=maxx+1;
128         }
129         else
130         {
131             maxx=0;
132             for(int j=1;j<=len;j++)
133             dp[i][j]=dp[i-1][j];
134             for(int j=b[i]+1;j<=a[i];j++)
135             dp[i][j]++;
136             for(int j=a[i]+1;j<=len;j++)
137             maxx=max(maxx,dp[i][j]);
138             dp[i][a[i]]=maxx+1;
139         }
140     }
141     int ans=0;
142     for(int i=1;i<=n;i++)
143     for(int j=1;j<=len;j++)
144     ans=max(ans,dp[i][j]);
145     cout<<ans<<endl;*/
146     build(1,1,len);
147     for(int i=1;i<=n;i++)
148     {
149         if(a[i]<=b[i])
150         modify(1,a[i],query(1,b[i]+1,len)+1);
151         else
152         update(1,b[i]+1,a[i],1),modify(1,a[i],query(1,a[i]+1,len)+1);
153     }
154     cout<<tr[1].mx;
155 }
T1

 

T2影魔

40分做法可以直接暴力搜索,一个一个跳;

说下正解,我的做法是用两个动态开点线段树,第一个每个节点开一颗,里面是深度,而维护的是在这个节点的子树中该深度的颜色是第一次出现的个数

(可持久化一下)

第二个是在该节点的子树中这种颜色出现的第一深度

用dfs进行合并,合并第二种树的时候,假如两个颜色都存在了,那么我们要在第一棵深度更深的那个地方-1,因为合并之后有比它深度更小的点出现了

然后就是一些板子类的东西

noip模拟14_i++noip模拟14_i++_02
#include<bits/stdc++.h>
using namespace std;
inline 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<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
const int maxn=1e5+1;
int n,m;
int a[maxn],num,tot,to[maxn],head[maxn],nxt[maxn];
int fa[maxn];
int dep[maxn];
int root1[maxn*60],root2[maxn*60];
struct seg_tree{
    int l,r,val;
}tr[maxn*120];
void add(int x,int y)
{
    to[++num]=y;
    nxt[num]=head[x];
    head[x]=num;
}
inline void build(int &x,int l,int r,int pos,int val)
{
    x=++tot;
    if(l==r)
    {
        tr[x].val=val;
        return ;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) build(tr[x].l,l,mid,pos,val);
    else build(tr[x].r,mid+1,r,pos,val);
}
inline void update(int &x,int l,int r,int pos,int val)
{
    ++tot;
    tr[tot]=tr[x];
    x=tot;
    tr[x].val+=val;
    if(l==r)
    return ;
    int mid=(l+r)>>1;
    if(pos<=mid) update(tr[x].l,l,mid,pos,val);
    else update(tr[x].r,mid+1,r,pos,val);
}
inline int merge1(int x,int y,int l,int r,int p)
{
    if(!x||!y)
    return x|y;
    int rt=++tot;
    if(l==r)
    {
        tr[rt].val=min(tr[x].val,tr[y].val);
        update(root2[p],1,n,max(tr[x].val,tr[y].val),-1);
        return rt;
    }
    int mid=(l+r)>>1;
    tr[rt].l=merge1(tr[x].l,tr[y].l,l,mid,p);
    tr[rt].r=merge1(tr[x].r,tr[y].r,mid+1,r,p);
    return rt;
}
inline int merge2(int x,int y,int l,int r)
{
    if(!x||!y)
    return x|y;
    int rt=++tot;
    tr[rt].val=tr[x].val+tr[y].val;
    int mid=(l+r)>>1;
    tr[rt].l=merge2(tr[x].l,tr[y].l,l,mid);
    tr[rt].r=merge2(tr[x].r,tr[y].r,mid+1,r);
    return rt;
}
inline int query(int p,int l,int r,int ll,int rr)
{
    if(ll<=l&&r<=rr)
    return tr[p].val;
    int mid=(l+r)>>1,ans=0;
    if(ll<=mid)
    ans+=query(tr[p].l,l,mid,ll,rr);
    if(rr>mid)
    ans+=query(tr[p].r,mid+1,r,ll,rr);
    return ans;
}
void dfs1(int x)
{
    build(root1[x],1,n,a[x],dep[x]);
    update(root2[x],1,n,dep[x],1);
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        dep[y]=dep[x]+1;
        dfs1(y);
        root1[x]=merge1(root1[x],root1[y],1,n,x);
        root2[x]=merge2(root2[x],root2[y],1,n);
    }
}
int main()
{
    //freopen("a.in","r",stdin);
    n=read(),m=read();
    for(int i=1;i<=n;i++)
    a[i]=read();
    for(int i=2;i<=n;i++)
    fa[i]=read(),add(fa[i],i);
    dep[1]=1; dfs1(1);
    int dis,x;
    for(int i=1;i<=m;i++)
    {
        x=read(),dis=read();
        printf("%d\n",query(root2[x],1,n,dep[x],dep[x]+dis));
    }
}
T2

T3抛硬币

其实是个挺水的n方dp,hash暴力扫一便可以50分

dp[ i ][ j ]表示处理到i,匹配了j个字符的方案,那么如果不去重,那么dp[ i ][ j ]=dp[ i-1 ][ j-1 ]+dp[ i-1 ][ j ]

然后考虑去重,也就是减去上个同样字符出现时候的方案dp[ i ][ j ]-=dp[ per-1 ][ j-1 ],per是i这个字符上一次出现的位置

noip模拟14_i++noip模拟14_i++_02
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline 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<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
const int mod=998244353;
char s[4001];
int len;
int du[30],zm,l;
int cf[4001];
int dp[4001][4001];
signed main()
{
    scanf("%s",s+1);
    len=strlen(s+1);
    l=read();
    for(int i=0;i<=len;i++)
    dp[i][0]=1;
    for(int i=1;i<=len;i++)
    {
        int now=s[i]-'a'+1;
        cf[i]=du[now];
        du[now]=i;
    }
    for(int i=1;i<=len;i++)
    {
        for(int j=1;j<=l;j++)
        {
            int now=cf[i];
            dp[i][j]=dp[i-1][j]+dp[i-1][j-1]%mod;
            if(now)
            ((dp[i][j]-=dp[now-1][j-1])+=mod)%=mod;
            
        }
    }
    cout<<dp[len][l];
    return 0;
}
T3

 

 
 
 
 
posted on 2021-07-16 15:53  JYFHYX  阅读(0)  评论(0)  编辑  收藏  举报