期望得分: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<<' '; } 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 ",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]; }