H Merge the squares!
题意
给定 n×n 个 1×1 的小正方形组成的大正方形,每次合并不超过 50 个小正方形得到一个大正方形。问如何合成能得到 n×n 的大正方形。
数据范围:1≤n≤1000。
题解
为了得到一个 i×i 的正方形,考虑这样进行合并:分拆成 j×j,(i−j)×j,j×(i−j),(i−j)×(i−j) 四个部分。也就是两个小正方形和两个相同的矩形。两个小正方形是子问题,可以递归解决,我们考虑处理矩形:有一种比较直觉的策略,即辗转相减。以长为 a,宽为 b 的矩形为例,切出一些 b×b 的小正方形,然后得到一个长为 b,宽为 amodb 的小矩形。只要这些总和不超过 24(24×2+1+1),就是一种合法的划分方式。幸运地是,对于每个 i 都能找到一个合法的 j。剩下的只是一些模拟工作了。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include <bits/stdc++.h> using namespace std; const int N=1005; int f[N]; vector<array<int,3>> ans; bool check(int x,int y) { if(!y) return x<=7; int cnt=0,tmp; while(y) { cnt+=x/y; tmp=x%y,x=y,y=tmp; } return cnt<=24; } void solve1(int x,int y,int a,int b); void solve2(int x,int y,int a,int b); void solve(int x,int y,int k) { if(k==1) return; ans.push_back({x,y,k}); if(!f[k]) return; solve1(x,y+f[k],f[k],k-f[k]),solve2(x+f[k],y,k-f[k],f[k]); solve(x,y,f[k]),solve(x+f[k],y+f[k],k-f[k]); } void solve1(int x,int y,int a,int b) { if(a<=1) return; for(int i=0;i<=b-a;i+=a) solve(x,y+i,a); solve2(x,y+b/a*a,a,b%a); } void solve2(int x,int y,int a,int b) { if(b<=1) return; for(int i=0;i<=a-b;i+=b) solve(x+i,y,b); solve1(x+a/b*b,y,a%b,b); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n;cin>>n; for(int i=2;i<=n;i++) for(int j=0;j<=i/2;j++) if(check(i-j,j)) { f[i]=j; break; } solve(1,1,n); reverse(ans.begin(),ans.end()); cout<<ans.size()<<'\n'; for(auto i:ans) cout<<i[0]<<' '<<i[1]<<' '<<i[2]<<'\n'; return 0; }
|
I Portal 3
题意
给定 n 个点,点 i 与点 j 的距离为 di,j。现在可以选择两个点 x,y 使得 dx,y=dy,x=0。另给定一个序列 a1,a2,⋯,ak,要求从 a1 出发依次走到每个点,直到最终到达 ak。求最短行走距离。
数据范围:2≤n≤500,2≤k≤106。
题解
首先Floyd求出两点间的距离 di,j,并且求出在行走序列中每条边 (i,j) 出现的次数 c(i,j)。然后,考虑枚举修改的边 (u,v),那么对于每对 (s,t),这次修改的贡献就是 cs,t×max(0,max(ds,t−ds,u−dv,t,ds,t−ds,v−du,t)),而后面两项最多只有一项为正,所以可以把贡献拆开算,即 cs,t×(max(0,ds,t−ds,u−dv,t)+max(0,ds,t−ds,v−du,t))。考虑计算 cs,t×(max(0,ds,t−ds,u−dv,t)):我们可以枚举 s,然后按 ds,u 对 u 进行排序。然后枚举 t 和 v(v 按 dv,t 的顺序枚举),不难发现当 v 变化时,能取到的 u 一定是一段前缀,这就可以用差分维护了。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pii; const int N=505; int d[N][N],c[N][N]; ll sum,tot[N][N],diff1[N][N],diff2[N][N]; pii tmp[N]; vector<pii> g[N]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n,m;cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>d[i][j]; for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) d[i][j]=min(d[i][j],d[i][k]+d[k][j]); int lst=0; for(int i=1;i<=m;i++) { int v;cin>>v; if(i!=1) c[lst][v]++,sum+=d[lst][v]; lst=v; } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) g[i].push_back({d[j][i],j}); sort(g[i].begin(),g[i].end()); } for(int s=1;s<=n;s++) { memset(diff1,0,sizeof(diff1)),memset(diff2,0,sizeof(diff2)); for(int u=1;u<=n;u++) tmp[u]={d[s][u],u}; sort(tmp+1,tmp+n+1); for(int t=1;t<=n;t++) { int j=n; for(int i=0;i<n;i++) { int v=g[t][i].second; ll cur=d[s][t]-g[t][i].first; while(j>=1&&cur-tmp[j].first<0) j--; diff1[1][v]+=c[s][t]*cur,diff1[j+1][v]-=c[s][t]*cur;diff2[1][v]+=c[s][t],diff2[j+1][v]-=c[s][t]; } } for(int i=1;i<=n;i++) for(int v=1;v<=n;v++) { diff1[i][v]+=diff1[i-1][v],diff2[i][v]+=diff2[i-1][v]; tot[tmp[i].second][v]+=diff1[i][v]; tot[tmp[i].second][v]-=diff2[i][v]*tmp[i].first; } } ll mx=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) mx=max(mx,tot[i][j]+tot[j][i]); cout<<sum-mx<<'\n'; return 0; }
|