1. 程式人生 > >POJ 2991 Crane(線段樹+計算幾何)

POJ 2991 Crane(線段樹+計算幾何)

Description
有一臺起重機。我們把起重機看作由N條線段依次首尾相接而成。第i條線段的長度是Li。最開始,所有的線段都筆直連線,指向上方。現有C條操縱起重機的指令。指令i給出兩個整數Si和Ai,效果是使線段Si和Si+1之間的角度變成Ao度。其中角度指的是從線段Si開始沿逆時針方向旋轉到Si+1所經過的角度。最開始所有角度都是180度。按順序執行這C條指令。在每條指令執行之後,輸出起重機的前段(第N條線段的端點)的座標。假設起重機的支點座標是(0,0)
Input
多組用例,每組用例第一行為兩個整數N和C分別表示起重機的線段數和指令數,第二行為N個整數表示各線段的長度,之後C行每行兩個整數表示指令,以檔案尾結束輸入
Output


對於每組用例,輸出執行完一條指令後起重機前段的座標,用空行隔開兩組輸出
Sample Input
2 1
10 5
1 90
3 2
5 5 5
1 270
2 90
Sample Output
5.00 10.00

-10.00 5.00
-5.00 10.00
Solution

說實話線段樹好學,線段樹的思想不好學吶,此題:線段樹的節點維護的是向量(座標),偏角

更新的操作是根據偏角的改變來更改向量的值——數學公式——計算幾何

首先建樹:

void build(int rt,int left,int right)
{
    //tx,ty座標
    //angle
    angle[rt] = tx[rt] = 0.0;

    if(left + 1 == right)
    {
        ty[rt] = L[right] * 1.0;
        return;
    }
    int mid = (left + right) >> 1;
    build(lson);
    build(rson);
    ty[rt] = ty[2*rt+1] + ty[2*rt + 2];
}

 偏轉更新——1 2 -- 2 3 由 1 3來控制 偏轉節點是2 =(1 + 3 ) / 2,0 1 -- 1 2有0 2 控制 控制節點是1,一個小節點的旋轉,就要向上更新是的每一個有關的節點旋轉

void update(int rt,int left,int right,int s,double a)
{
    //cout<<rt<<" "<<left<<" "<<right<<endl;
    if(s > left && s < right)
    {
        //cout<<"繼續向前"<<endl;
        int mid = (left + right) >> 1;
        update(lson,s,a);
        update(rson,s,a);
        //因為最後輸出的是n節點也就是頭節點的座標也就是向量的值,所以所有的更新都為了線段樹的根節點!!
        if(s <= mid)//對上面照成的影響問題//相當於上面的向量都旋轉了angle度
            angle[rt] += a;

        int sonl = rt * 2 + 1;
        int sonr = rt * 2 + 2;
        //cout<<"past:"<<endl;
        //cout<<rt<<" "<<tx[rt]<<" "<<ty[rt]<<endl;
        double ss = sin(angle[rt]),c = cos(angle[rt]);
        //cout<<ss<<" "<<c<<endl;
        tx[rt] = tx[sonl] + (c * tx[sonr] - ss * ty[sonr]);
        ty[rt] = ty[sonl] + (ss * tx[sonr] + c * ty[sonr]);
        //cout<<"NOW"<<endl;
        //cout<<rt<<" "<<tx[rt]<<" "<<ty[rt]<<endl;
    }
    //cout<<"不在該區間"<<endl;
}

 code......

#include <cstdio>
#include <string.h>
#include <cmath>
#include <iostream>
#define lson 2*rt+1,left,mid
#define rson 2*rt+2,mid,right
using namespace std;
const int maxn = (1 << 15) - 1;
const int maxm = 10001;
const double PI = acos(-1.0);
int n,c;
int L[maxm],s[maxm],alpha[maxm];
double tx[maxn],ty[maxn];
double angle[maxn],preangle[maxm];

void init()
{
    memset(tx,0,sizeof(tx));
    memset(ty,0,sizeof(ty));
    memset(preangle,0,sizeof(preangle));
    memset(angle,0,sizeof(angle));
    for(int i = 0;i <= n;++i)
    {
        preangle[i] = PI;//初始夾角
    }
}
void build(int rt,int left,int right)
{
    //tx,ty座標
    //angle
    angle[rt] = tx[rt] = 0.0;

    if(left + 1 == right)
    {
        ty[rt] = L[right] * 1.0;
        return;
    }
    int mid = (left + right) >> 1;
    build(lson);
    build(rson);
    ty[rt] = ty[2*rt+1] + ty[2*rt + 2];
}
void update(int rt,int left,int right,int s,double a)
{
    //cout<<rt<<" "<<left<<" "<<right<<endl;
    if(s > left && s < right)
    {
        //cout<<"繼續向前"<<endl;
        int mid = (left + right) >> 1;
        update(lson,s,a);
        update(rson,s,a);
        //因為最後輸出的是n節點也就是頭節點的座標也就是向量的值,所以所有的更新都為了線段樹的根節點!!
        if(s <= mid)//對上面照成的影響問題//相當於上面的向量都旋轉了angle度
            angle[rt] += a;

        int sonl = rt * 2 + 1;
        int sonr = rt * 2 + 2;
        //cout<<"past:"<<endl;
        //cout<<rt<<" "<<tx[rt]<<" "<<ty[rt]<<endl;
        double ss = sin(angle[rt]),c = cos(angle[rt]);
        //cout<<ss<<" "<<c<<endl;
        tx[rt] = tx[sonl] + (c * tx[sonr] - ss * ty[sonr]);
        ty[rt] = ty[sonl] + (ss * tx[sonr] + c * ty[sonr]);
        //cout<<"NOW"<<endl;
        //cout<<rt<<" "<<tx[rt]<<" "<<ty[rt]<<endl;
    }
    //cout<<"不在該區間"<<endl;
}
int main()
{
    int cas = 0;
    while(~scanf("%d%d",&n,&c))
    {

        init();
        cas++;

        for(int i = 1;i <= n;i++)
            scanf("%d",&L[i]);
        for(int i = 0;i < c;i++)
            scanf("%d%d",&s[i],&alpha[i]);
        build(0,0,n);
        for(int i = 0;i < c;i++)
        {
            //轉化為弧度
            double nowalpha = alpha[i] / 180.0 * PI;
            //更新
            update(0,0,n,s[i],nowalpha - preangle[s[i]]);
            preangle[s[i]] = nowalpha;
            //輸出節點座標(向量)
            printf("%.2lf %.2lf\n",tx[0],ty[0]);
        }
        if(cas != 1)
            printf("\n");
    }
    return 0;
}