题目:
ICPC 2019-2020 North-Western Russia Regional Contest
E:Equidistant
题意:
给定一颗树,再给出 m 个关键点,让你找出一个点使得这个点到 m 个关键点的距离相等
分析:
(1)直观的做法就是换根dp,预处理出每个节点为根时,子树中的关键点到它距离最大值和最小值,次最大值和次最小值,换根dp的时候直接看所有关键点到根的最大值和最小值是否相等
(2)问题可以转换为找到一个点,使得所有关键点到它距离的最大值最小,这个点一定是关键点的直径上的中点,这个点一定是唯一的,先两边 dfs 找到直径,再找到中点,最后判断一下这个中点是否满足题意即可
代码:
#include
#define f first
#define s second
#define pii pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 2e5+25;
int head[maxn],n,m,u,v,cnt,d[maxn],tag[maxn],p[maxn];
struct edge{
int to,nxt;
}e[maxn<<1];
inline void add(int u,int v){
e[++cnt] = (edge){v,head[u]};
head[u] = cnt;
}
int x,len;
void dfs(int u,int fa,int step){
if(tag[u]&&step>=len){
x = u; len = step;
}
for(int i = head[u];i > 0;i = e[i].nxt){
int v = e[i].to; if(v == fa) continue;
dfs(v,u,step+1);
}
}
bool flag;
int q[maxn],k;
void dfs2(int u,int fa){
q[k++] = u;
if(u == x){
flag = true; return;
}
for(int i = head[u];i>0&&!flag;i = e[i].nxt){
int v = e[i].to; if(v == fa) continue;
dfs2(v,u);
}
if(!flag) k--;
}
void dfs3(int u,int fa,int depth){
d[u] = depth;
for(int i = head[u];i > 0;i = e[i].nxt){
int v = e[i].to; if(v == fa) continue;
dfs3(v,u,depth+1);
}
}
int main(){
scanf("%d %d",&n,&m);
for(int i = 1;i < n; ++i){
scanf("%d %d",&u,&v);
add(u,v); add(v,u);
}
for(int i = 1;i <= m; ++i){
scanf("%d",p+i); tag[p[i]] = 1;
}
if(m == 1){
puts("YES"); puts("1"); return 0;
}
dfs(p[1],0,0); int y = x; len = 0;
dfs(y,0,0); dfs2(y,0);
if(k % 2 == 0) return puts("NO");
else k /= 2;
dfs3(q[k],0,1);
for(int i = 2;i <= m; ++i){
if(d[p[i]] != d[p[i-1]]) return puts("NO");
}
puts("YES");
cout << q[k] << '\n';
return 0;
}
H:High Load Database
题意:
给定 N 个数,让你将这些数分成连续的块,每一块的和 <= t,求最少可以分成多少块
分析:
知道 t 的值,可以O(N)贪心的求得最少的块数,但现在有多个 t,那么就不能直接扫一遍,考虑优化一下;对原数组求一遍前缀和,那么每次就可以二分得到下一块边界的位置,那么这样复杂度的上限就是总的块数*logN;对于每一个 t,它的块数在 N/t 左右,对于相同的 t 只求一遍,那么这就是一个调和级数了;复杂度O(NlogNlogN)
想了半天才发现(太菜了)
代码:
#include
#define f first
#define s second
#define pii pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 1e6+15;
const int mod = 1e9+7;
int n,q,t,m,a[maxn],sum[maxn],ans[maxn];
int solve(int t){
int res = 0,pos = 0,num;
while(pos < n){
num = sum[pos]+t;
pos = lower_bound(sum+pos+1,sum+n+1,num)-sum;
if(sum[pos] > num) pos--;
res++;
}
return res;
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n; ++i){
scanf("%d",a+i); m = max(m,a[i]);
sum[i] = sum[i-1] + a[i];
}
scanf("%d",&q);
while(q--){
scanf("%d",&t);
if(t < m) puts("Impossible");
else if(ans[t]) printf("%d\n",ans[t]);
else printf("%d\n",ans[t] = solve(t));
}
return 0;
}
I:Ideal Pyramid
题意:
给定 N 个柱子的坐标和高度,让你找到一个最小的四棱锥(斜面和底面夹角为45°),使得全部的柱子都在里面
分析:
由于四棱锥的斜面和底面夹角为45°,那么只要知道它的高度,它的形状就唯一确定了;那么就可以二分它的高度,考虑如何check,发现对于一个柱子要在它的里面,那么四棱锥的低面中心可以在这个柱子的周围为正方形的区域内,只要对 N 个正方形求一个交集即可
代码:
#include
#define f first
#define s second
#define pii pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 2000;
struct point{
LL x,y,h;
}p[maxn];
LL ansx,ansy,ans,n;
struct node{
LL x,y,xx,yy;
};
node merge(node a,node b){
return (node){max(a.x,b.x),max(a.y,b.y),min(a.xx,b.xx),min(a.yy,b.yy)};
}
bool check(LL mid){
LL t = mid - p[1].h;
node s = (node){p[1].x-t,p[1].y-t,p[1].x+t,p[1].y+t};
for(int i = 2;i <= n; ++i){
t = mid - p[i].h;
s = merge(s,(node){p[i].x-t,p[i].y-t,p[i].x+t,p[i].y+t});
if(s.xx < s.x || s.yy < s.y) return false;
}
ans = mid; ansx = s.x,ansy = s.y;
return true;
}
int main(){
cin >> n;
LL L = 0, R = 1e18;
for(int i = 1;i <= n; ++i){
cin>>p[i].x>>p[i].y>>p[i].h;
L = max(L,p[i].h);
}
while(L <= R){
LL mid = (L+R)>>1;
if(check(mid)) R = mid - 1;
else L = mid + 1;
}
cout << ansx << " " << ansy << " " << ans << '\n';
return 0;
}
J:Just the Last Digit
题意:
有一张有向无环图,给出每个点到另一个点的方案数的个位数,让你还原这张图
分析:
容易发现,s 到 e 的方案中要么直达,要么经过 k(s < k < e);为了不重复计算,只需要知道 s 能否直接到达 k ,能就加上 k到 e 的方案,最后判断是否需要直达即可
代码:
#include
#define f first
#define s second
#define pii pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 625;
char a[maxn][maxn];
int n,mp[maxn][maxn];
int main(){
cin >> n;
for(int i = 1;i <= n; ++i) scanf("%s",a[i]+1);
for(int s = 1;s <= n; ++s){
for(int e = s+1;e <= n; ++e){
int sum = 0;
for(int k = s+1;k < e; ++k){
if(mp[s][k]) sum += a[k][e]-'0';
}
if(sum%10 != a[s][e]-'0') mp[s][e] = 1;
}
}
for(int i = 1;i <= n; ++i){
for(int j = 1;j <= n; ++j)
printf("%d",mp[i][j]);
puts("");
}
return 0;
}