2021.07.07【NOIP提高B组】模拟 总结
第一题:首先预处理ci,jc_{i,j}ci,j表示第iii行选了jjj个的最大价值,这个可以用暴力或者动态规划求出。然后设fi,jf_{i,j}fi,j表示前iii行选了jjj个的答案,转移枚举kkk从kkk转移到jjj,注意优化。
第二题:考虑动态规划,设fi,jf_{i,j}fi,j表示前iii种颜色其中有jjj对颜色相邻且相等,考虑第i+1i+1i+1种颜色。设sis_isi表示前iii种颜色的个数和,aia_iai表示第iii种颜色的个数。把这种颜色分为kkk块,其中有l(l≤k,j)l(l\le k,j)l(l≤k,j)块插入到了原来相邻颜色中。有以下选择:
- 从这ai+1a_{i+1}ai+1个颜色中分成kkk组,插板问题,为Cai+1−1k−1C_{a_{i+1}-1}^{k-1}Cai+1−1k−1;
- 从这jjj对相邻的颜色中选择lll组,为CjlC_j^lCjl;
- 把剩下的k−lk-lk−l个插入在其他位置,其他位置有si−j+1s_i-j+1si−j+1(加上111是因为第一个位置前也可以插入),为Csi−j+1k−lC_{s_i-j+1}^{k-l}Csi−j+1k−l。
那么新的有多少个相邻颜色呢?
可以发现为j−l+ai+1−kj-l+a_{i+1}-kj−l+ai+1−k。那么可得fi+1,j−l+ai+1−k=fi,j×Cai+1−1k−1×Cjl×Csi−j+1k−lf_{i+1,j-l+a_{i+1}-k}=f_{i,j}\times C_{a_{i+1}-1}^{k-1}\times C_j^l\times C_{s_i-j+1}^{k-l}fi+1,j−l+ai+1−k=fi,j×Cai+1−1k−1×Cjl×Csi−j+1k−l。
预处理组合数就可以了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t,n,a[20],s[20];
ll f[20][100],c[100][100];
const int mod=1000000007;
inline ll C(register int x,register int y){
return c[x][y];
}
inline ll inv(register ll x){
register int y=mod-2;
register ll sum=1;
while (y){
if (y&1) sum*=x,sum%=mod;
x*=x,x%=mod;
y>>=1;
}
return sum;
}
int main(){
freopen("whitewasher.in","r",stdin);
freopen("whitewasher.out","w",stdout);
scanf("%d",&t);
c[0][0]=1;
for (register int i=1;i<=100;i++){
c[i][0]=1;
for (register int j=1;j<=i;j++){
c[i][j]=c[i-1][j-1]+c[i-1][j];
c[i][j]%=mod;
}
}
while (t--){
scanf("%d",&n);
for (register int i=1;i<=n;i++){
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
}
memset(f,0,sizeof(f));
f[0][0]=1;
for (register int i=1;i<=n;i++){
for (register int j=0;j<=min(s[i-1]+1,s[i]);j++){
for (register int k=1;k<=a[i];k++){
for (register int l=0;l<=min(j,min(k,j+a[i]-k));l++){
f[i][j+a[i]-k-l]+=((((f[i-1][j]*C(a[i]-1,k-1))%mod*C(j,l))%mod*C(s[i-1]+1-j,k-l))%mod);
f[i][j+a[i]-k-l]%=mod;
}
}
}
}
printf("%lld\n",f[n][0]);
}
}
第三题:设fi,sf_{i,s}fi,s表示当前树根为iii,状态为jjj的最小代价。设disi,jdis_{i,j}disi,j为输入的距离。
明显的转移:fi,s=fj,s′+fi,s−s′+disi,j(s′⊂s)f_{i,s}=f_{j,s'}+f_{i,s-s'}+dis_{i,j}(s'\subset s)fi,s=fj,s′+fi,s−s′+disi,j(s′⊂s)。
还有就是fi,s=fj,s+disi,jf_{i,s}=f_{j,s}+dis_{i,j}fi,s=fj,s+disi,j。
由于上面这个式子无法知道顺序,所以做一遍最短路。
然后我们只要把fff给合并起来,注意要保证iii和n−i+1n-i+1n−i+1必须同时在状态中或者同时不在,因为不在的话说明他们没有连通。
#include<bits/stdc++.h>
using namespace std;
int n,m,k,la[10005],to[20005],ne[20005],we[20005],cnt,f[500][10005],dis[10005],vis[10005];
int g[500],h[500];
const int inf=1000000000;
void add(int x,int y,int z){
++cnt;
to[cnt]=y;
ne[cnt]=la[x];
we[cnt]=z;
la[x]=cnt;
}
queue<int>q,q2;
void spfa(int st){
q=q2;
for (int i=1;i<=n;i++){
dis[i]=f[st][i];
vis[i]=0;
q.push(i);
}
while (!q.empty()){
int x=q.front();
vis[x]=0;
q.pop();
for (int i=la[x];i;i=ne[i]){
int y=to[i];
if (dis[x]+we[i]<dis[y]){
dis[y]=dis[x]+we[i];
if (!vis[y]){
vis[y]=1;
q.push(y);
}
}
}
}
}
bool check(int x){
for (int i=0;i<k;i++){
int f1=(1<<i)&x;
int f2=(1<<(i+k))&x;
if (f1>0&&f2==0) return 0;
if (f1==0&&f2>0) return 0;
}
return 1;
}
int main(){
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for (int i=1,x,y,z;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
for (int i=0;i<(1<<(2*k));i++)
for (int j=1;j<=n;j++)
f[i][j]=inf;
for (int i=k+1;i<=n-k;i++) f[0][i]=0;
for (int i=1;i<=k;i++){
f[1<<(i-1)][i]=0;
f[1<<(k+i-1)][n-i+1]=0;
}
for (int i=1;i<(1<<(2*k));i++){
for (int j=1;j<=n;j++){
for (int i1=i;i1;i1=(i1-1)&i){
if (f[i1][j]>=f[i][j]) continue;
if (i1!=i){
for (int j1=la[j];j1;j1=ne[j1]){
int j2=to[j1],j3=we[j1];
f[i][j]=min(f[i][j],f[i1][j]+f[i-i1][j2]+j3);
}
}
}
}
spfa(i);
g[i]=inf;
for (int j=1;j<=n;j++) f[i][j]=dis[j],g[i]=min(g[i],f[i][j]);
}
for (int i=1;i<(1<<(2*k));i++)h[i]=inf;
h[0]=0;
for (int i=1;i<(1<<(2*k));i++)
if (check(i))
for (int j=i;j;j=(j-1)&i)
if (check(i-j))
h[i]=min(h[i],h[i-j]+g[j]);
if (h[(1<<(2*k))-1]==inf) printf("-1");
else printf("%d",h[(1<<(2*k))-1]);
}
第四题:看到数据先离散化。考虑每个点上建一个线段树维护答案,那么就可以用差分修改答案,对于(x,y)(x,y)(x,y)以及lca(x,y)lca(x,y)lca(x,y)和fa(lca(x,y))fa(lca(x,y))fa(lca(x,y)),将前两个点加一,后两个点减一。但是这样空间肯定回报,考虑动态开点。差分之后要统计,统计时将线段树合并。所以用线段树合并的方法。注意一些细节。当然可以用树链剖分做。
#include<bits/stdc++.h>
using namespace std;
int n,m,la[100005],to[200005],ne[200005],cnt;
int a[100005],b[100005],c[100005],dep[100005],f[100005][17];
int t[2005][2005],drk[100005],rt[100005],ans[100005];
const int inf=1000000000;
struct food{
int val,id,rk;
}d[100005];
struct tree{
int val,ls,rs,id;
}tr[100005*80];
int sz=0;
void pushup(int x){
int ls=tr[x].ls;
int rs=tr[x].rs;
if (tr[ls].val>=tr[rs].val){
tr[x].val=tr[ls].val;
tr[x].id=tr[ls].id;
}
else{
tr[x].val=tr[rs].val;
tr[x].id=tr[rs].id;
}
}
void update(int &x,int l,int r,int k,int v){
if (!x) x=++sz;
if (l==r&&l==k){
tr[x].id=k;
tr[x].val+=v;
return;
}
int mid=(l+r)>>1;
if (k<=mid) update(tr[x].ls,l,mid,k,v);
else update(tr[x].rs,mid+1,r,k,v);
pushup(x);
}
void merge(int &x,int y,int l,int r){
if ((!x)||(!y)) x+=y;
else if (l==r) tr[x].val+=tr[y].val;
else{
int mid=(l+r)>>1;
merge(tr[x].ls,tr[y].ls,l,mid);
merge(tr[x].rs,tr[y].rs,mid+1,r);
pushup(x);
}
}
void add(int x,int y){
++cnt;
to[cnt]=y;
ne[cnt]=la[x];
la[x]=cnt;
}
bool cmp(food x,food y){
return x.val<y.val;
}
void dfs(int x,int y){
dep[x]=dep[y]+1;
f[x][0]=y;
for (int i=1;i<=16;i++) f[x][i]=f[f[x][i-1]][i-1];
for (int i=la[x];i;i=ne[i])
if (y!=to[i])
dfs(to[i],x);
}
void dfs2(int x,int y){
for (int i=la[x];i;i=ne[i]){
if (y!=to[i]){
dfs2(to[i],x);
merge(rt[x],rt[to[i]],1,d[m].rk);
}
}
if (tr[rt[x]].val) ans[x]=tr[rt[x]].id;
}
int lca(int x,int y){
if (x==y) return x;
if (dep[x]<dep[y]) swap(x,y);
int k=dep[x]-dep[y],s=0;
while (k){
if (k&1) x=f[x][s];
s++;
k>>=1;
}
if (x==y) return x;
for (int i=16;i>=0;i--)
if (f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1,x,y;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
for (int i=1;i<=m;i++){
scanf("%d%d%d",&a[i],&b[i],&c[i]);
d[i].val=c[i];
d[i].id=i;
}
sort(d+1,d+m+1,cmp);
d[0].val=-1;
for (int i=1;i<=m;i++)
if (d[i].val!=d[i-1].val) d[i].rk=d[i-1].rk+1;
else d[i].rk=d[i-1].rk;
for (int i=1;i<=m;i++){
int x=d[i].id;
drk[d[i].rk]=d[i].val;
c[x]=d[i].rk;
}
for (int i=1;i<=m;i++){
int x=lca(a[i],b[i]);
update(rt[a[i]],1,d[m].rk,c[i],1);
update(rt[b[i]],1,d[m].rk,c[i],1);
update(rt[x],1,d[m].rk,c[i],-1);
if (f[x][0])
update(rt[f[x][0]],1,d[m].rk,c[i],-1);
}
dfs2(1,0);
for (int i=1;i<=n;i++){
printf("%d\n",drk[ans[i]]);
}
}