吐嘈:啦啦啦,hale又菜又弱,最近补CF题觉得自己快傻了
小奇挖矿2(mining) 【题目背景】 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿石交易市场,以便为飞船升级无限非概率引擎。 【问题描述】 现在有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 【样例输出】 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
第一题:
显然六十分是白给的,学过DP的都会。。。
说说正解吧,我们上去第一眼就觉得这个很像跳石子那道题,于是大家都考虑对坐标进行压缩,这明显在emmmm
于是我们,忽然灵光乍现,是不是很像小凯的疑惑,嘿嘿,这便是我觉得最精妙的地方
我们先处理出来4,7两个能表示出来的步数,我们发现他最小不表示出来的是17
于是以此为契机,进行暴力转移,考虑前面的如何转移过来,维护一个最大值数组即可qwq
时间复杂度O(n);
#include#define I inline using namespace std; #define LL long long const int N=1e5+7; LL mx[N],dp[N],w[N]; int m,n; LL ans; struct node { int pos,cost; bool operator<(const node &x) const { return pos<x.pos; } } t[N]; bool vis[19]; I int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { freopen("mining.in","r",stdin); freopen("mining.out","w",stdout); n=read();m=read(); for (int i=1;i<=n;i++) t[i].cost=read(),t[i].pos=read(); sort(t+1,t+n+1); vis[0]=true; for (int i=1;i<=19;i++) if (vis[i-4]||vis[i-7]) vis[i]=true; for (int i=1;i<=n;i++) { int k=1; while (true) { int p=i-k;k++; if (p<0) break; int dis=t[i].pos-t[p].pos; if (dis>=18) { dp[i]=max(dp[i],mx[p]+t[i].cost); break; } else if (vis[dis]) dp[i]=max(dp[i],dp[p]+t[i].cost); } mx[i]=max(mx[i-1],dp[i]); } LL ans=0; for (int i=1;i<=n;i++) ans=max(ans,mx[i]); printf("%lld\n",ans); return 0; }
第二题:
刚开始的时候便只写了个暴搜,预计三十分,
但是又看了看这个矩阵里面的元素大小,哎呀,很奇怪,只有30以下,这一定是突破口
于是我们正解也就呼之欲出了
dp[i][j][k]表示到达坐标为(i,j)这个点,元素总和为k的元素平方的和
我们为了搞掉他的平均数的后效型,对原式子进行转化
那么答案不难推出便是:min((n+m+1)*dp[i][j][k]-k*k)
直接暴力转移即可
#includeusing namespace std; int n,m,T; int a[50][50]; int dp[50][50][2000]; int ans,maxn; void init() { ans=1<<30; memset(dp,0x3f,sizeof(dp)); } int sqr(int x) {return x*x;} int main() { freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); scanf("%d",&T); while(T--) { init(); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]); dp[1][1][a[1][1]]=sqr(a[1][1]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=a[i][j];k<=1800;k++) dp[i][j][k]=min(dp[i][j][k],min(dp[i-1][j][k-a[i][j]],dp[i][j-1][k-a[i][j]])+sqr(a[i][j])); for(int i=a[n][m];i<=1800;i++) if(dp[n][m][i]<1061109567) ans=min(ans,dp[n][m][i]*(n+m-1)-i*i); printf("%d\n",ans); } return 0; }
第三题:
说句实话,真心写不出来满分。。。。。。。。(hale是个傻X)
我们考虑部分分吧
对于前三十分:直接对于每个点进行暴力计算,时间复杂度O(n^2);
对于M=1的情况:我们变相考虑一条边对于这个树的影响,不难发现每次减少量为,当前边的左右两边节点差值
我们钦定1为根节点,先暴力求出问题答案,之后转移即可
时间复杂度:O(n)
#include#define I inline using namespace std; const int N=2e5+7; int m,n,ans[N],cnt,head[N],M,size[N]; struct edge { int nx,to,dis; } e[N]; void add_edge(int a,int b,int dist) { cnt++;e[cnt].nx=head[a];e[cnt].to=b;e[cnt].dis=dist;head[a]=cnt; } I int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();} return x*f; } I void write(int x) { if(x<0){putchar('-');x=-x;} if(x>9) write(x/10); putchar(x%10+'0'); } void dfs1(int x,int fa,int dis,int p) { for (int i=head[x];i;i=e[i].nx) { int y=e[i].to; if (y==fa) continue; ans[p]+=(dis+e[i].dis)^M; dfs1(y,x,dis+e[i].dis,p); } } void solve1() { for (int i=1;i<=n;i++) dfs1(i,0,0,i); for (int i=1;i<=n;i++) write(ans[i]),puts(""); } void dfs(int x,int fa) { size[x]=1; for (int i=head[x];i;i=e[i].nx) { int y=e[i].to; if (y==fa) continue; dfs(y,x); size[x]+=size[y]; } } void dfs2(int x,int fa) { for (int i=head[x];i;i=e[i].nx) { int y=e[i].to; if (y==fa) continue; ans[y]=ans[x]+e[i].dis*size[y]; dfs2(y,x); } } void solve2() { dfs1(1,0,0,1); dfs(1,0); for (int i=1;i<=n;i++) size[i]=n-2*size[i]; dfs2(1,0); for (int i=1;i<=n;i++) write(ans[i]),puts(""); } int main() { freopen("warehouse.in","r",stdin); freopen("warehouse.out","w",stdout); n=read(),M=read(); for (int i=1;i ) { int x=read(),y=read(),z=read(); add_edge(x,y,z);add_edge(y,x,z); } if (M!=0) solve1(); else solve2(); return 0; }
正解:(真心不会了,最后膜的大佬代码)
我们还是考虑,一个边对于一个树的影响,异或只影响后四位,于是我们就直接把边的边权拆了
然后再最后总和出来就好了,(这么智障的东西都搞不出来。。。)
#include#define LL long long #define I inline using namespace std; const int N=2e5+7; int M,n,cnt,head[N]; int ans[N],g[N][16],dp[N],size[N]; I int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();} return x*f; } inline void write(int x) { if(x<0){putchar('-');x=-x;} if(x>9) write(x/10); putchar(x%10+'0'); } struct edge { int nx,to,dis; } e[N]; void add_edge(int a,int b,int dis) { cnt++;e[cnt].nx=head[a];e[cnt].to=b;e[cnt].dis=dis;head[a]=cnt; } void dfs1(int x,int fa) { for (int i=head[x];i;i=e[i].nx) { int y=e[i].to;if (y==fa) continue; dfs1(y,x); dp[x]+=dp[y]+e[i].dis/16; g[x][e[i].dis%16]++; for (int j=0;j<16;j++) { int k=j+e[i].dis; dp[x]+=k/16*g[y][j]; g[x][k%16]+=g[y][j]; } } } void dfs2(int x,int fa) { for (int i=head[x];i;i=e[i].nx) { int y=e[i].to;if (y==fa) continue; int tmp=dp[x]-dp[y]; for (int j=0;j<16;j++) { int k=j+e[i].dis; tmp-=(k/16)*g[y][j]; size[k%16]=g[x][k%16]-g[y][j]; } size[e[i].dis%16]--; dp[y]+=tmp; g[y][e[i].dis%16]++; for (int j=0;j<16;j++) { int k=j+e[i].dis; dp[y]+=k/16*size[j]; g[y][k%16]+=size[j]; } dfs2(y,x); } } int main() { freopen("warehouse.in","r",stdin); freopen("warehouse.out","w",stdout); n=read(),M=read(); for (int i=1;i ) { int x=read(),y=read(),z=read(); add_edge(x,y,z);add_edge(y,x,z); } dfs1(1,0); dfs2(1,0); for (int i=1;i<=n;i++) { LL ans=dp[i]*16; for (int j=0;j<16;j++) ans+=(j^M)*g[i][j]; write(ans);puts(""); } return 0; }