小奇挖矿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]);
}
}
小奇的矩阵
【题目背景】
小奇总是在数学课上思考奇怪的问题。
【问题描述】
给定一个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;
}
小奇的仓库
【题目背景】
小奇采的矿实在太多了,它准备在喵星系建个矿石仓库。令它无语的是,喵星系的货运飞船引擎还停留在上元时代!
【问题描述】
喵星系有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;
}