gym102021 2018GCPC 部分題解

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.
明顯有
b1=a1+a2,b2=a2+a3,b3=a3+a4,b4=a4+a5;b1=a1+a2,b2=a2+a3,b3=a3+a4,b4=a4+a5;
則 a1=a1,a1=a1,
  a2=b1a1,a2=b1-a1,
  a3=b2a2=b2b1+a1,a3=b2-a2=b2-b1+a1,
  a5=b4a4=b4b3+b2b1+a1;a5=b4-a4=b4-b3+b2-b1+a1;
這時可以發現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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章