cf Round #766(Div. 2)
B
Description
n*m个方格,一个人尽量往中间坐没涂色的格子,另一个人往四角坐,求涂上0,1,2,...,n*m-1个格子后两人曼哈顿距离的最大值。
Solution
看到曼哈顿距离,会自然而然考虑将(x,y)对称到左上角(x',y')后的x'+y'。
按x'+y'升序排序后即为涂色的顺序。相同x'+y'涂色后的答案相同。
只剩一个格子没涂色时答案为dis((1,1),(n,m))。x'+y'减k则答案也减k。
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
struct grid{
int x,y,ans;
}a[N];
int n,m,t,cnt;
int dis(grid a){
return min(a.x-1,n-a.x)+min(a.y-1,m-a.y);
}
bool cmp_xy(grid a,grid b){
return dis(a)>dis(b);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
cnt=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
a[++cnt].x=i;a[cnt].y=j;
}
sort(a+1,a+1+cnt,cmp_xy);
for(int i=cnt,ans=(n-1)+(m-1);i;--i){
if(i<cnt&&dis(a[i])!=dis(a[i+1])) --ans;
a[i].ans=ans;
}
for(int i=1;i<=cnt;++i)
printf("%d ",a[i].ans);
printf("\n");
}
return 0;
}
C
Description
一棵树,求给每条边赋权值使任意长度\(\leq2\)的路径权值和都为质数。
Solution
由于质数的奇偶性,所以当某个点的度>2时无解。
由于保证连通性,所以树是一条链。交替填入2,5即可。
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
struct graph{
int nxt,to,w;
}e[N<<1];
int g[N],deg[N],n,t,cnt;
void adde(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].w=0;
}
void dfs(int u,int num){
for(int i=g[u];i;i=e[i].nxt)
if(!e[i].w){
e[i].w=e[i^1].w=(num==2?5:2);
dfs(e[i].to,e[i].w);
}
}
int main(){
scanf("%d",&t);
while(t--){
cnt=1;
memset(g,0,sizeof(g));
memset(deg,0,sizeof(deg));
scanf("%d",&n);
for(int i=1,x,y;i<n;++i){
scanf("%d%d",&x,&y);
adde(x,y);adde(y,x);++deg[x];++deg[y];
}
bool flag=true;
for(int i=1;i<=n;++i)
if(deg[i]>2){
flag=false;break;
}
if(!flag){
printf("-1\n");continue;
}
for(int i=1;i<=n;++i)
if(deg[i]==1){
dfs(i,2);
}
for(int i=1;i<n;++i)
printf("%d ",e[i<<1].w);
printf("\n");
}
return 0;
}
D
Description
给定一组互不相同的数,往里面一直加入任意两个数的gcd直到不能加入为止。求加入的数的个数。
Solution
最开始想的是如何处理加入一个新数带来的变化。
后来发现结果是确定的只用考虑每个数是否会存在于结果中即可。
由于gcd(n,m)<min{n,m},从大到小考虑每个数。
一个数k存在的条件是存在两个数a,b,是>k的k的倍数且a/k与b/k互质,即gcd(a,b)=k。
又因为所有>k的k的倍数\(a_i\)满足\(gcd(a_1,...,a_n)\geq k\),所以如果存在a,b,\(gcd(a_1,...,a_n)= k\)。
埃氏筛处理即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1000001;
int n,ans;
bool b[N];
int main(){
scanf("%d",&n);
for(int i=1,k;i<=n;++i){
scanf("%d",&k);
b[k]=true;
}
for(int i=N-1;i;--i){
int g=0;
for(int j=i;j<N;j+=i)
if(b[j]){
if(!g) g=j;
else g=__gcd(g,j);
}
if(g==i) b[i]=true;
if(b[i]) ++ans;
}
printf("%d\n",ans-n);
return 0;
}
E
Description
一栋楼,同层之间可以平移,会损失体力,不同层之间可以到特定位置走梯子,会收获体力(梯子只能往上走)。求(1,1)到(n,m)的最小损失体力值。
Solution
离散化所有梯子的起点和终点以及(1,1)和(n,m)。
dis[i]表示到达i点的最小损失体力值。
很显然的是,不同层之间独立,从下往上考虑每一层。
每一层中,一个点到另一个点直达最优,所以可以两个方向分别扫一遍求出dis[i]。
然后再更新以这一层为起点的梯子能到达的dis[i]。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
const ll INF=1e18;
struct ladder{
int a,b,c,d,h;
}a[N];
int x[N],n,m,k,t,pn;
ll dis[N<<1];
pair<int,int> p[N<<1];
bool cmp(ladder a,ladder b){
return a.a<b.a;
}
int search(pair<int,int> x){
int l=1,r=pn,mid;
while(l<r){
mid=(l+r)>>1;
if(p[mid]<x) l=mid+1;
else r=mid;
}
return l;
}
int main(){
scanf("%d",&t);
while(t--){
pn=0;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;++i)
scanf("%d",&x[i]);
for(int i=1;i<=k;++i){
scanf("%d%d%d%d%d",&a[i].a,&a[i].b,&a[i].c,&a[i].d,&a[i].h);
p[++pn]=make_pair(a[i].a,a[i].b);
p[++pn]=make_pair(a[i].c,a[i].d);
}
p[++pn]=make_pair(1,1);
p[++pn]=make_pair(n,m);
sort(p+1,p+1+pn);
pn=unique(p+1,p+1+pn)-p-1;
sort(a+1,a+1+k,cmp);
for(int i=1;i<=pn;++i) dis[i]=INF;
dis[1]=0;
for(int i=1,j=1,ik=1;i<=pn;i=j){
while(j<=pn&&p[i].first==p[j].first) ++j;
for(int l=i+1;l<j;++l)
if(dis[l-1]<INF) dis[l]=min(dis[l],dis[l-1]+1ll*(p[l].second-p[l-1].second)*x[p[l].first]);
for(int l=j-2;l>=i;--l)
if(dis[l+1]<INF) dis[l]=min(dis[l],dis[l+1]+1ll*(p[l+1].second-p[l].second)*x[p[l].first]);
while(ik<=k&&a[ik].a==p[i].first){
if(dis[search(make_pair(a[ik].a,a[ik].b))]<INF)
dis[search(make_pair(a[ik].c,a[ik].d))]=min(dis[search(make_pair(a[ik].c,a[ik].d))],dis[search(make_pair(a[ik].a,a[ik].b))]-1ll*a[ik].h);
++ik;
}
}
if(dis[pn]!=INF) printf("%lld\n",dis[pn]);
else printf("NO ESCAPE\n");
}
return 0;
}