GCPC
詳細題解
A. Attack on Alpha-Zet(lca)
題意:
問按序號從小到大走完全程需要走多少格,保證兩點之間只有一條路
思路: 轉化建樹,因爲題目保證任何兩點之間只有一條路,lca即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005;
char mp[N][N*2];
int n,m;
int head[N*N],cnt;
struct node
{
int v,nxt;
}edge[N*N*2];
int id(int x,int y)
{
return (x-1)*m+(y+1)/2;
}
void add(int u,int v)
{
edge[cnt].v=v;
edge[cnt].nxt=head[u];
head[u]=cnt++;
}
int grand[N*N][22]={0};
int depth[N*N],DEPTH,sum=1;
void dfs(int x)
{
for(int i=1;i<=DEPTH;i++)
{
grand[x][i]=grand[grand[x][i-1]][i-1];
}
for(int i=head[x];i!=-1;i=edge[i].nxt)
{
int to=edge[i].v;
if(grand[x][0]==to)continue;
depth[to]=depth[x]+1;
grand[to][0]=x;
dfs(to);
}
}
void init(int s)
{
DEPTH=floor(log(n*m + 0.0) / log(2.0));
depth[s]=1;
memset(grand,0,sizeof(grand));
dfs(s);
}
int lca(int a,int b)
{
if(depth[a]>depth[b])swap(a,b);
for(int i=DEPTH;i>=0;i--)
if(depth[a]<depth[b]&&depth[grand[b][i]]>=depth[a])
b=grand[b][i];
for(int i=DEPTH;i>=0;i--)
if(grand[a][i]!=grand[b][i])
{
a=grand[a][i];
b=grand[b][i];
}
if(a!=b)
{
return grand[a][0];
}
return a;
}
int main()
{
scanf("%d%d",&n,&m);
getchar();
memset(head,-1,sizeof head);
for(int i=0;i<=n;i++)
{
gets(mp[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m*2;j+=2)
{
if(mp[i][j+1]!='|')
{
add(id(i,j),id(i,j+2));
add(id(i,j+2),id(i,j));
}
if(mp[i][j]!='_')
{
add(id(i,j),id(i+1,j));
add(id(i+1,j),id(i,j));
}
}
}
init(1);
ll res=0;
int q,now,last;
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int x,y;
scanf("%d%d",&x,&y);
now=id(x,y*2-1);
if(i!=1)
{
int tmp=lca(last,now);
res+=depth[last]+depth[now]-2*depth[tmp];
}
last=now;
}
printf("%lld\n",res);
return 0;
}
D. Down the Pyramid(思維)
題意: 給出數字金字塔的第一層,問有多少種合法的第二層的數的方案。
思路:
因爲ai≥0,所以這些條件會將x限定在一個範圍內,這個範圍內的數的個數就是方案數。
假設給出n=4的序列b1,……,bn.你要求的就是它下方的一層a1,……,an+1.
明顯有
則
這時可以發現a序列的確定僅與a1相關,換句話說求a序列的數目也就是求a1的數目。問題轉換成了求解求a1的範圍
#include <iostream>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1000000 + 5;
int b[maxn];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n;i++)
cin >> b[i];
int mina1 = 0, maxa1 = INF;
int temp = 0;
for (int i = 1; i <= n;i++)
{
temp = b[i] - temp;
if(i%2)
maxa1 = min(maxa1, temp);
else
mina1 = max(mina1, -temp);
}
cout << mina1 << " " << maxa1 << endl;
if(maxa1>=mina1)
cout << maxa1 - mina1 + 1;
else
cout << 0;
return 0;
}
K.Kitchen Cable Chaos(01揹包)
題意: 有兩個器械之間的距離爲g,現在有n根線段,他們的長度爲di(包含兩端5cm的金屬箔),線段與線段之間需要用金屬箔接觸相連接,一段連接出來的電纜,它的質量爲連接處的最短的重疊部分的長度。
電纜最後也要和器械的金屬箔相接觸,這一段重疊部分也被納入質量的考量。
問如果選擇線段,最後得到的質量最高。
像這張圖裏面,1號連接處是完全覆蓋的,他們的重疊部分爲5,2號連接處他們是恰好接觸的,重疊部分爲0,所以這段線纜的質量爲0。
思路: 那麼假設選了i根線,那麼一共有i+1個接觸部分,我們要讓重疊部分儘量均攤,這樣最小值最大。用fi,j表示用了i個物品,能否拼出j。 01揹包即可。
#include <bits/stdc++.h>
using namespace std;
int dp[100][10000];
int s[10000];
int main() {
int n, g;
cin >> n >> g;
int all = g + (n + 1) * 5;
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
dp[0][10] = 1;
for (int i = 1; i <= n; i++) {
for (int j = i-1; j >= 0; j--) {
for (int k = 0; k + s[i] <= all; k++) {
dp[j + 1][k + s[i]] |= dp[j][k];
}
}
}
double ans = -1;
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= all; j++) {
if (!dp[i][j]) continue;
if (j < g || j > g + (i + 1) * 5) continue;
ans = max(ans, double(j - g) / (i + 1));
}
}
if (ans >= 0) printf("%.7f\n", ans);
else printf("impossible\n");
return 0;
}
M. Mountaineers(lca)
題意: 在一個n⋅m的矩形上,每個點有一個權值hi,每次可以上下左右移動,有q次詢問,每次詢問給出起點和終點,詢問從起點到終點的路徑中經過的點的最大權值的最小值是多少?
思路: 將點按照權值排序,然後從小到大進行建樹,大的是小的祖先,
#include<bits/stdc++.h>
#define N 250005
using namespace std;
struct edge{int v,w;};
vector<edge>edges[N];
int grand[N][25]={0};
int depth[N],DEPTH,sum=1;
void addedge(int a,int b)
{
edges[b].push_back((edge){a});
}
void dfs(int x)
{
for(int i=1;i<=DEPTH;i++)
{
grand[x][i]=grand[grand[x][i-1]][i-1];
}
for(int i=0;i<edges[x].size();i++)
{
int to=edges[x][i].v;
if(grand[x][0]==to)continue;
depth[to]=depth[x]+1;
grand[to][0]=x;
dfs(to);
}
}
void init(int s)
{
DEPTH=floor(log(sum + 0.0) / log(2.0));
depth[s]=1; //注意根節點的深度不要設爲0,否則下面判深度會出錯
memset(grand,0,sizeof(grand));
dfs(s);
}
int lca(int a,int b)
{
if(depth[a]>depth[b])swap(a,b);
for(int i=DEPTH;i>=0;i--)
if(depth[a]<depth[b]&&depth[grand[b][i]]>=depth[a])
b=grand[b][i];
for(int i=DEPTH;i>=0;i--)
if(grand[a][i]!=grand[b][i])
{
a=grand[a][i];
b=grand[b][i];
}
if(a!=b)
{
return grand[a][0];
}
return a;
}
int pre[300000];
int Find(int x)
{
int boss=x;
while(boss!=pre[boss])boss=pre[boss];
int temp;
while(x!=pre[x])
{
temp=pre[x];
pre[x]=boss;
x=temp;
}
return boss;
}
struct ss
{
int x,y,value,number;
bool operator < (const ss &s) const
{
return value<s.value;
}
};
ss arr[300000];
int vis[520][520]={0};
int ans[300000];
int main()
{
int m,n,q,s;
scanf("%d %d %d",&m,&n,&q);
for(int i=0;i<=m*n;i++)pre[i]=i;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&arr[sum].value);
arr[sum].x=i;
arr[sum].y=j;
arr[sum].number=sum;
ans[sum]=arr[sum].value;
sum++;
}
sort(arr+1,arr+sum);
for(int i=1;i<sum;i++)
{
int x=arr[i].x,y=arr[i].y,num=arr[i].number;
vis[x][y]=num;
s=num;
if(x-1>=1&&vis[x-1][y]&&Find(vis[x-1][y])!=num)
{
addedge(Find(vis[x-1][y]),num);
pre[Find(vis[x-1][y])]=num;
}
if(x+1<=m&&vis[x+1][y]&&Find(vis[x+1][y])!=num)
{
addedge(Find(vis[x+1][y]),num);
pre[Find(vis[x+1][y])]=num;
}
if(y-1>=1&&vis[x][y-1]&&Find(vis[x][y-1])!=num)
{
addedge(Find(vis[x][y-1]),num);
pre[Find(vis[x][y-1])]=num;
}
if(y+1<=n&&vis[x][y+1]&&Find(vis[x][y+1])!=num)
{
addedge(Find(vis[x][y+1]),num);
pre[Find(vis[x][y+1])]=num;
}
}
init(s);
while(q--)
{
int a,b,c,d;
scanf("%d %d %d %d",&a,&b,&c,&d);
a--;
c--;
printf("%d\n",ans[lca(a*n+b,c*n+d)]);
}
return 0;
}