HNOI的背景,只不过题目不一样
T1队长快跑
不难看出一个n方dp,dp[ i ][ j ]表示处理到 i 的时候最小的a为j的情况,
然后分情况进行讨论就好了,
发现处理第i层的时候首先要继承前一层的状态,然后再判断是否要摧毁,那么这个题我们可以用线段树优化一下查询和修改的操作
复杂度可以降低到nlog(n);
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 }
T2影魔
40分做法可以直接暴力搜索,一个一个跳;
说下正解,我的做法是用两个动态开点线段树,第一个每个节点开一颗,里面是深度,而维护的是在这个节点的子树中该深度的颜色是第一次出现的个数
(可持久化一下)
第二个是在该节点的子树中这种颜色出现的第一深度
用dfs进行合并,合并第二种树的时候,假如两个颜色都存在了,那么我们要在第一棵深度更深的那个地方-1,因为合并之后有比它深度更小的点出现了
然后就是一些板子类的东西
#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)); } }
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这个字符上一次出现的位置
#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; }