【NOIP模拟赛】小奇颓废赛 Day2

D2T1

小奇挖矿2

【题目背景】
小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿石交易市场,以便为飞船升级无限非概率引擎。
 【问题描述】
现在有m+1个星球,从左到右标号为0到m,小奇最初在0号星球。
有n处矿体,第i处矿体有ai单位原矿,在第bi个星球上。
由于飞船使用的是老式的跳跃引擎,每次它只能从第x号星球移动到第x+4号星球或x+7号星球。每到一个星球,小奇会采走该星球上所有的原矿,求小奇能采到的最大原矿数量。
注意,小奇不必最终到达m号星球。

输入
第一行2个整数n,m。
接下来n行,每行2个整数ai,bi。

输出
输出一行一个整数,表示要求的结果。

样例输入
3 13
100 4
10 7
1 11

样例输出 Copy
101

提示
【样例解释】
第一次从0到4,第二次从4到11,总共采到101单位原矿。
 
【数据范围】
对于20%的数据 n=1,m<=10^5
对于40%的数据 n<=15,m<=10^5
对于60%的数据 m<=10^5
对于100%的数据 n<=10^5,m<=10^9,1<=ai<=10^4,1<=bi<=m

初眼思路:搜索。。。

打出来之后不敢交,最后水了二十分

正解:线性动态规划

  • 因为我们只能走4步或者7步,根据小凯的疑惑定理,易知4*7-4-7=18之后的步数我们都可以到达,于是我们可以先根据距离排序,然后把两点之间大于17的点之间设为18

  • 然后我们只需要从头开始进行动态规划就可以了,可是为什么要这样进行动态规划呢

  • 我们只能走4或者7步,不走一定不是最优解,所以我们考虑走4步或者走7步,当前走4步也许比走7步要优,但是走7步在后面也许是最优解,这是我们怎样理解这个转移方程呢

  • 因为我们在后面仍然可以更新,最终我们当前走的这4步,后面走7步的最优解会更新出来,所以确保了我们转移方程的正确性

核心代码

for(int i=1;i<=n;i++) {
        if(e[i].b-e[i-1].b>17) now+=18,v[now]+=e[i].a;
        else now+=e[i].b-e[i-1].b,v[now]+=e[i].a;
    }
    f[0]=0;
    for(int i=0;i<=now;i++) {
        if(f[i]!=-1) {
            f[i+4]=max(f[i+4],f[i]+v[i+4]);
            f[i+7]=max(f[i+7],f[i]+v[i+7]);
            ans=max(ans,f[i]);
        }
    }

D2T2

小奇的矩阵

【题目背景】
小奇总是在数学课上思考奇怪的问题。
 【问题描述】
给定一个n*m的矩阵,矩阵中的每个元素aij为正整数。
接下来规定
1.合法的路径初始从矩阵左上角出发,每次只能向右或向下走,终点为右下角。
2.路径经过的n+m-1个格子中的元素为A1,A2…A(n+m-1),Aavg为Ai的平均数,路径的V值为(n+m-1)*∑(Ai-Aavg) ^2
(1<=i<=n+m-1)
求V值最小的合法路径,输出V值即可,有多组测试数据。

输入
第一行包含一个正整数T,表示数据组数。
对于每组数据:
第一行包含两个正整数n和m,表示矩阵的行数和列数。
接下来n行,每行m个正整数aij,描述这个矩阵。

输出
对于每次询问,输出一行一个整数表示要求的结果

样例输入
1
2 2
1 2
3 4

样例输出
14

提示
对于30%的数据 n<=10,m<=10
有另外40%的数据 n<=15 m<=15,矩阵中的元素不大于5
对于100%的数据 T<=5,n<=30,m<=30,矩阵中的元素不大于30

初眼思路:多维动态规划

但是题目中给的n+m-1我不知道是乘在里面还是乘在外面,我把每种情况都试了,没有一个对住样例的,于是我就放弃了,怪我咯

正解:多维动态规划

  • 我们首先把题目中所给的式子变形Ans=(n+m−1)(A12+A22+…+An+m−12)−2Sum2+Sum2=(n+m−1)(A12+A22+…+An+m−12)−Sum^2
    转载于此地

  • 我们定义一个三维数组f[i][j][k]来记录当前坐标记录的总和值为k的最小值(我也是第一次遇到这样的动态规划题,涨知识了)

核心代码

f[1][1][map[1][1]]=map[1][1]*map[1][1];
for(int i=1;i<=n;i++) {
	for(int j=1;j<=m;j++) {
		if(i==1&&j==1) continue;
		for(int k=map[i][j];k<=1770;k++) {
			f[i][j][k]=min(f[i-1][j][k-map[i][j]],f[i][j-1][k-map[i][j]])+map[i][j]*map[i][j];
		}
	}
}
ans=1e9;
for(int k=0;k<=1770;k++) {
	tot=1ll*(n+m-1)*f[n][m][k]-k*k;
	if(ans>tot) ans=tot;
}

D2T3

小奇的仓库

【题目背景】
小奇采的矿实在太多了,它准备在喵星系建个矿石仓库。令它无语的是,喵星系的货运飞船引擎还停留在上元时代! 
【问题描述】
喵星系有n个星球,星球以及星球间的航线形成一棵树。
从星球a到星球b要花费[dis(a,b) Xor M]秒。(dis(a,b)表示ab间的航线长度,Xor为位运算中的异或)
为了给仓库选址,小奇想知道,星球i(1<=i<=n)到其它所有星球花费的时间之和。

输入
第一行包含两个正整数n,M。
接下来n-1行,每行3个正整数a,b,c,表示a,b之间的航线长度为c。

输出
n行,每行一个整数,表示星球i到其它所有星球花费的时间之和。

样例输入
4 0
1 2 1
1 3 2
1 4 3

样例输出
6
8
10
12
测试点编号 N M
1 6 0
2 100 5
3 2000 9
4 50000 0
5 50000 0
6 50000 1
7 50000 6
8 100000 10
9 100000 13
10 100000 15

保证答案不超过2*10^9

初眼思路:倍增

类似于同时处理f和dis数组,但是我打了两个多小时,在自己造的数据中还是挂了,最后想打个树的遍历,没时间了,于是成功爆0

正解:树形动态规划

据说是两遍dfs就可以了,大佬说是换根DP,我也听不懂,最终在百思不得其解之后放弃


摘自大佬题解

首先考虑不异或

那么我们先算出根节点1到所有点的距离和

接下来假设2是1儿子,边为<1,2,w>,size[i]为i的子树的大小

ans[2]=ans[1]+(n-size)w-sizew=ans[1]+(n-2*size)*w

但本题加了异或,异或不满足分配律,所以不能算出再异或

但我们发现m很小,最多也就2^4-1,也就是1111

异或只会影响后4位

于是设f[i][j]为i到其它点,后4位状态为j的路径条数

显然列出:

f[u][0]=1

f[u][(j+w)%16]+=f[v][j]

这还只算了子树

f[v][(j+w)%16]+=(f[u][j]-f[v][((j-w)%16+16)%16])

解释一下,u这个点的所有f[u][j]除去v的方案再加入v,等于把v的方案

从子树补到整棵

AC代码

#include
#define N 100005
using namespace std;
struct edge {
	int next,to,l;
}e[N<<1];
int n,m,cnt,head[N],f[N],g[N],sz[N],up[N][16];
inline void add(int x,int y,int z) {
	e[++cnt].to=y,e[cnt].l=z,e[cnt].next=head[x],head[x]=cnt;
}
inline void dfs1(int x,int fa) {
	sz[x]=1;
	for(int i=head[x];i;i=e[i].next) {
		int y=e[i].to,l=e[i].l;
		if(y==fa) continue;
		dfs1(y,x);
		g[x]+=g[y]+l*sz[y];
		sz[x]+=sz[y];
		for(int j=0;j<16;j++) {
			int k=j+l;k-=k>>4<<4;
			up[x][k]+=up[y][j];
		}
		up[x][l-(l>>4<<4)]++;
	}
}
inline void dfs2(int x,int fa,int cnt) {
	g[x]+=f[x]; int sum=g[x];
	int s=cnt+sz[x],d[20];
	for(int i=head[x];i;i=e[i].next) {
		int y=e[i].to,l=e[i].l;
		if(y==fa) continue;
		f[y]=sum-g[y]+l*(s-sz[y]*2);
		for(int i=0;i<16;i++) d[i]=up[x][i];
		for(int j=0;j<16;j++) {
			int k=j+l;k-=k>>4<<4;
			d[k]-=up[y][j];
		}
		d[l-(l>>4<<4)]--;
		for(int j=0;j<16;j++) {
			int k=j+l;k-=k>>4<<4;
			up[y][k]+=d[j];
		}
		up[y][l-(l>>4<<4)]++;
		dfs2(y,x,s-sz[y]);
	}
}
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++) {
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),add(y,x,z);
	}
	dfs1(1,0);
	dfs2(1,0,0);
	for(int i=1;i<=n;i++) {
		for(int j=0;j<16;j++) {
			int k=j^m;
			g[i]+=(k-j)*up[i][j];
		}
		printf("%d\n",g[i]);
	}
	return 0;
}

最终得分20+0+0=20,还有四十天就要2019CSP了,慌得一批,还是好好学习吧,在还没有成为定局之前就是应该一直拼搏

你可能感兴趣的:(模拟赛)