2019.01.26【NOIP提高组】模拟 A&B 组

前言

由于A&B组题目相同,所以说一起写,但是貌似……A组第三题不会做


B组部分

JZOJ 1252 洛谷 5194 天平

题目

nn个数中选择若干个,使它们的和不超过cc且最大


分析

倒序dfs,并用前缀和优化


代码

#include <cstdio>
#include <algorithm>
#define rr register
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n; long long c,a[41],s[41],ans;
inline void dfs(int dep,long long sum){
	if (s[dep-1]<=c-sum) ans=max(ans,s[dep-1]+sum);
	else{
		ans=max(ans,sum);
		for (rr int i=dep-1;i;--i)
		if (c-sum>=a[i]) dfs(i,sum+a[i]);
	}
}
signed main(){
	scanf("%d%lld",&n,&c);
	for (rr int i=1;i<=n;++i){
		scanf("%lld",&a[i]);
		if (a[i]>c) i--,n--;
		else s[i]=s[i-1]+a[i];
	}
	dfs(n+1,0); 
	return !printf("%lld",ans);
}

JZOJ 1274 游历路线

题目

从1号点到nn号点,不能停留,在每一个周期每条单向路有一定的费用,问到第mm天的最小费用


代码(线性动态规划)

#include <cstdio>
#include <cstring>
#include <cctype>
#define rr register
using namespace std;
int n,m,w[101][101][21],dp[201][101];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline signed min(int a,int b){return (a<b)?a:b;}
signed main(){
	freopen("lines.in","r",stdin);
	freopen("lines.out","w",stdout);
	n=iut(); m=iut();
	memset(w,127/3,sizeof(w));
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<=n;++j) if (i!=j){
		w[i][j][0]=iut();
		for (rr int k=1;k<=w[i][j][0];++k){
		    w[i][j][k]=iut();
		    if (!w[i][j][k]) w[i][j][k]=707406378;
		}
	}
	memset(dp,127/3,sizeof(dp)); dp[0][1]=0;
	for (rr int i=1;i<=m;++i)
	for (rr int j=1;j<=n;++j) if (dp[i-1][j]!=707406378)
	for (rr int now=1;now<=n;++now) if (j!=now)
		dp[i][now]=min(dp[i][now],dp[i-1][j]+w[j][now][(i-1)%w[j][now][0]+1]);
	if (dp[m][n]!=707406378) printf("%d",dp[m][n]); else putchar(48);
	return 0;
} 

A&B组部分

JZOJ 4224 食物

题目

2019.01.26【NOIP提高组】模拟 A&B 组


分析

那么只要美味度满足要求的食物大小总和可以用这些工具装好,那么计算工具的最小费用即为答案,为什么呢,因为食物可以拆开,但是不满足部分背包
所以这道题可以分成两部分

  1. 找到满足美味度的最小的空间
  2. 求出工具的最小费用

这些都可以通过多重背包(二进制拆分变为01背包)实现,但是问题是直接维护空间很难完成第二步,于是可以玄学的把它反转,设f[j]f[j]表示空间为jj的最大美味度,那么容易得到f[j]=f[jw[i]]+c[i],c[i]w[i]f[j]=f[j-w[i]]+c[i],c[i]表示美味度,w[i]表示空间,然后只要f[j]pf[j]\geq p那么对jj取最小值,然后取到这个最小值再用一遍多重背包求得答案


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int M=50000; int f[M+105],dp[M+5];
struct rec{int w,c;}a[1201],b[1201];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void min(int &a,int b){if (a>b) a=b;}
inline void max(int &a,int b){if (a<b) a=b;}
signed main(){
	for (rr int t=iut();t;--t){
		rr int n1=iut(),n2=iut(),p=iut(),i1,poi=M+1,ans=M+1;
		for (i1=n1,n1=0;i1;--i1){//二进制拆分
		    rr int w=iut(),c=iut(),t=iut();
		    for (rr int j=1;t>=j;t-=j,j<<=1)
		    	a[++n1]=(rec){w*j,c*j};
		    if (t) a[++n1]=(rec){w*t,c*t};
		}
		for (i1=n2,n2=0;i1;--i1){
		    rr int c=iut(),w=iut(),t=iut();
		    for (rr int j=1;t>=j;t-=j,j<<=1)
		    	b[++n2]=(rec){w*j,c*j};
		    if (t) b[++n2]=(rec){w*t,c*t};
		}
		fill(f+1,f+M+101,100001); f[0]=0;
		for (rr int i=1;i<=n1;++i)
		for (rr int j=M+100;j>=a[i].w;--j){//可能还会超过一点点
		    min(f[j],f[j-a[i].w]+a[i].c);
		    if (j>=p) min(poi,f[j]);
		}
		fill(dp+1,dp+M+1,-100001); dp[0]=0;
		for (rr int i=1;i<=n2;++i)
		for (rr int j=ans;j>=b[i].w;--j){
		    max(dp[j],dp[j-b[i].w]+b[i].c);
		    if (dp[j]>=poi) min(ans,j);
		}
	    if (ans>M) printf("TAT\n"); else printf("%d\n",ans);
	}
	return 0;
} 

A组部分

JZOJ 4223 旅游

题目

询问有多少个无序点对(x,y)(x,y)至少有一条最大边权不超过dd的路径


分析

那么这道题适合离线处理,用并查集维护连通块的大小,从小到大排序边权,从小到大排序dd,然后对于每一个询问,多出来的就是(son[x]+son[y])×(son[x]+son[y]1)(son[x]×(son[x1])+son[y]×(son[y]1))(son[x]+son[y])\times(son[x]+son[y]-1)-(son[x]\times (son[x-1])+son[y]\times (son[y]-1))


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
struct node{int x,y,w;}e[100001];
struct rec{int w,rk;}a[5001];
int n,m,q,f[20001],son[20001],ans[5001];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline signed getf(int u){return (f[u]==u)?u:f[u]=getf(f[u]);}
bool cmp1(node a,node b){return a.w<b.w;}
bool cmp2(rec x,rec y){return x.w<y.w;}
signed main(){
	for (rr int t=iut();t;--t){
		n=iut(); m=iut(); q=iut();
		for (rr int i=1;i<=m;++i) e[i]=(node){iut(),iut(),iut()};
		for (rr int i=1;i<=n;++i) son[f[i]=i]=1;
		for (rr int i=1;i<=q;++i) a[i]=(rec){iut(),i};
		sort(e+1,e+1+m,cmp1); sort(a+1,a+1+q,cmp2); rr int j=1;
		for (rr int i=1;i<=q;++i){
			ans[a[i].rk]=ans[a[i-1].rk];
			while (j<=m&&e[j].w<=a[i].w){
				rr int fa=getf(e[j].x),fb=getf(e[j].y);
				if (fa!=fb){
					ans[a[i].rk]-=son[fa]*(son[fa]-1)+son[fb]*(son[fb]-1);
					if (fa>fb) fa^=fb,fb^=fa,fa^=fb;
					son[f[fa]=fb]+=son[fa];
					ans[a[i].rk]+=son[fb]*(son[fb]-1);
				}
				++j;
			}
		}
		for (rr int i=1;i<=q;++i) print(ans[i]),putchar(10);
	}
	return 0;
}

JZOJ 4225 宝藏

题目(简化版)

询问树上一个点到另外一个点的期望长度


分析

未完待续


代码


后续

我明明菜,dalaodalao还不承认