CF1955H The Most Reckless Defense
给敌人加血可以看成是减少防御塔的攻击力,那么一个塔对敌人能造成的最大伤害即为 \(500\pi r^2-3^r\),注意到 \(r=12\) 时已经小于 \(0\) 了,所以半径不会很大,又因为每个 \(r\) 只能选一次,所以有效的塔很少,考虑状压 \(dp\)。
具体地,我们设 \(f_{i,S}\) 表示前 \(i\) 个塔中,被选到的塔的半径集合为 \(S\),所造成的伤害(攻击力)为多少。显然最后答案就等于最大的伤害。有转移方程:
\[f_{i,S}=\max(f_{i-1,S},f_{i-1,S\oplus r}+p_i\times calc(i,r)-3^r)
\]
含义很简单,就是当前选或不选。\(calc(i,r)\) 表示第 \(i\) 座塔半径为 \(r\) 时能攻击到几次敌人。预处理 \(calc\) 的复杂度为 \(\mathcal{O}(k\cdot R^3)\),\(dp\) 的复杂度为 \(\mathcal{O}(k\cdot R\cdot 2^R)\),总复杂度 \(\mathcal{O}(k\cdot R\cdot (R^2+2^R))\)。
其实也可以最后一起减去 \(\sum 3^r\) 的,两种都对。一开始我不理解为啥两种写法是等价的,想了想,大概是因为每次取 \(\max\) 的两项只差一个 \(r\),所以要么都减了,要么都没减,不影响相对大小,也就不影响结果。
其实这题还能费用流。每个塔作为左部点,\(12\) 个半径作为右部点,边权或者说费用就是 \(p_i\times calc(i,r)-3^r\),跑一遍二分图带权最大匹配,也就是最大费用最大流即可。不过我没写,口胡的。
\(dp\) 代码:
#include<bits/stdc++.h> #define FOR(i,a,b) for(register int i=(a);i<=(b);++i) #define ROF(i,a,b) for(register int i=(a);i>=(b);--i) #define mp(a,b) make_pair(a,b) #define pll pair<long long,long long> #define pii pair<int,int> #define fi first #define se second #define eb emplace_back #define ep emplace using namespace std; inline int read(); typedef long long ll; typedef double db; const int N=50+5; const int inf=0x3f3f3f3f; const int INF=0x7f7f7f7f; const int R=12; int sqr(int x){return (x)*(x);} int n,m,k; char s[N][N]; struct tower{ int x,y,p; }a[N*N]; int f[N*N][1<<R]; int calc[N*N][R+1]; int Calc(int x,int y,int r){ int res=0; FOR(i,max(x-r,1),min(n,x+r)) FOR(j,max(y-r,1),min(m,y+r)) if(s[i][j]=='#'&&sqr(x-i)+sqr(y-j)<=sqr(r)) res++; return res; } void work(){ n=read(),m=read(),k=read(); FOR(i,1,n) scanf("%s",s[i]+1); FOR(i,1,k) a[i]={read(),read(),read()}; FOR(i,1,k){ int x=a[i].x,y=a[i].y; FOR(r,1,R) calc[i][r]=Calc(x,y,r); } memset(f,-0x3f,sizeof f); f[0][0]=0; FOR(i,1,k){ for(int S=0;S<(1<<R);++S){ f[i][S]=f[i-1][S]; FOR(r,1,R) if(S>>(r-1)&1) f[i][S]=max(f[i][S],f[i-1][S^(1<<r-1)]+a[i].p*calc[i][r]-(int)pow(3,r)); } } int ans=0; FOR(i,1,k) FOR(S,0,(1<<R)-1) ans=max(ans,f[i][S]); printf("%d\n",ans); } int main() { int T=read(); while(T--) work(); return 0; //あまりに短い夏だけで何を残していけるのかな? } inline 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<<3)+(x<<1)+(ch^48);ch=getchar();} return f*x; }
$$\text{看完了不点个赞再走嘛!!!}$$
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现