2015年8月21日 星期五

[TIOJ 1603] 胖胖殺蚯事件

原題:http://tioj.ck.tp.edu.tw/problems/1603
AC:http://tioj.ck.tp.edu.tw/submissions/17785

解題方向:RMQ


題目大意:區間最大值減最小值

解法:RMQ裸題,沒有修改,所以想要線段樹、Treap、莫對、塊狀練表等等做一做就會AC了,可以拿來實驗很多新方法的題目XD。這裡使用的是Sparse Table,因為沒有修改,顯得特別有優勢。

看到學弟最近在寫這題,無聊來刷了一下Rank~




列舉各方法複雜度
方法 建構資料 查詢 備註
Sparse Table $n\log n$ $1$ 黑魔法加持
線段樹 $n\log n$ $\log n$
樹堆 $n\log n$ $\log n$ 常數大
莫對數組 $n\log n+n\sqrt{n}$ $1$
塊狀鍊表 $n\sqrt{n}$ $\sqrt{n}$

學高級技巧時,也別忘了最單純的想法


#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
ll spM[17][100001];
ll spm[17][100001];
int main()
{
    #ifdef DBG
    //freopen("case.in", "r" , stdin);
    #endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int N,Q,gap,jmp;
    cin>>N>>Q;
    for(int i=1;i<=N;++i)
    {
        cin>>spM[0][i];
        spm[0][i]=spM[0][i];
    }
    for(int i=1; (1<<i)<=N; ++i )
    {
        jmp = ( 1<<(i-1) );
        for(int j=1; j+jmp<=N; ++j )
        {
            spM[i][j] = max( spM[i-1][j] , spM[i-1][j+jmp] ); 
            spm[i][j] = min( spm[i-1][j] , spm[i-1][j+jmp] );
        }
    }
    while(Q--)
    {
        int L,R;
        cin>>L>>R;
        gap = 31 - __builtin_clz(abs(L-R)+1);
        cout<< max( spM[gap][L],spM[gap][R-(1<<gap)+1] )
              -min( spm[gap][L],spm[gap][R-(1<<gap)+1] ) << '\n';
    }
}
同場加映線段樹
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
pii data[400000];
pii merge(const pii &a,const pii &b)
{
    return { min(a.first,b.first) , max(a.second,b.second) };
}

void update(int i,ll v,int L,int R,int I)
{
    if(L==R){
        data[I]={v,v};
        return ;
    }
    int M = (L+R)/2;
    if(i<=M)update(i,v,L,M,I*2+1);
    else update(i,v,M+1,R,I*2+2);
    data[I] = merge(data[I*2+1],data[I*2+2]);
}

pii find(int l,int r,int L,int R,int I)
{
    if( L==l && R==r ) return data[I];
    int M = (L+R)/2;
    if ( r<=M ) return find(l,r,L  ,M,I*2+1);
    if ( M< l ) return find(l,r,M+1,R,I*2+2);
    return merge(   find(l  ,M,L  ,M,I*2+1),
                    find(M+1,r,M+1,R,I*2+2));
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int N,M,a,b;
    ll t;
    cin>>N>>M;
    for(int i=1;i<=N;++i)
    {
        cin>>t;
        update(i,t,1,N,0);
    }
    while(M--)
    {
        cin>>a>>b;
        auto p = find(a,b,1,N,0);
        cout<<p.second-p.first<<'\n';
    }
    return 0;
}

2015年8月19日 星期三

[TIOJ 1396] 線段覆蓋

原題:http://tioj.ck.tp.edu.tw/problems/1396
AC:http://tioj.ck.tp.edu.tw/submissions/17674

解題方向:單調性,貪心法


題目大意:兩個人分別有不同的兩組線段,有標記放置的位置,試問誰能用最少的線段覆蓋指定區間。

解法:我們可以分開計算兩個人所需的線段,把線段排序後,由左向右掃描,檢查最右界是否能到達終點即可。

本方法最費時間的為排序,總複雜度為$O(n\log n)$

透過特判可以把時間壓到$140ms$,不過$visitorIKC$的$61ms$我還沒頭緒,是不是能在優化到線性複雜度呢? (對方表示他只是極致的壓常數


#include<bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    pair<int,int> d[2][100000];
    int N[2];
    int X,Y;
    int ans[2];
    register int i,j,mx,ed;
    bool flag;
    while( cin>>N[0]>>N[1] )
    {
        ans[0]=ans[1]=INT_MAX;
        for(i=0;i<2;++i)
            for(j=0;j<N[i];++j)
                cin>>d[i][j].first>>d[i][j].second;
        cin>>X>>Y;
        flag = false;
        if( X == Y ) {
            for(i=0;i<100000000;++i);
            for(i=0;i<2;++i)
                for(j=0;j<N[i];++j)
                    if( d[i][j].first <= X && X <= d[i][j].second )
                    {
                        ans[i] = 1 ;
                        break;
                    }
        }
        else
        {
            sort(d[0],d[0]+N[0]);
            sort(d[1],d[1]+N[1]);
            for(i=0;i<2;++i)
            {
                ed = mx = X;
                j = 0;
                while( j!=N[i] && ed < Y )
                {
                    ans[i]++;
                    ed = mx;
                    flag = false;
                    while( j!=N[i] && d[i][j].first <= ed ) {
                        if( d[i][j].second > mx )
                        {
                            flag = true;
                            mx = d[i][j].second;
                        }
                        j++;
                    }
                    if( !flag ) break;
                }
                if( mx < Y ) ans[i] = INT_MAX;
            }
        }
        if( ans[0] == ans[1] && ans[0] == INT_MAX ) cout<<"TIE\n";
        else if( ans[0] <= ans[1] ) cout<<"A WIN\n";
        else cout<<"B WIN\n";
    }
}

2015年8月15日 星期六

[Codeforces][559B][560D] B. Equivalent Strings

原題:http://codeforces.com/problemset/problem/559/B
AC:http://codeforces.com/contest/559/submission/12532597

解題方向:模擬


題目大意:給你兩個等長的字串,定義兩字串$A,B$等價,是滿足下其中之一的條件:

  1. $A = B$
  2. 若能將A分成等長的字串$A1,A2$,也把B分成$B1,B2$,則滿足其中之一:
  • $A1$等價$B1$,且$A2$等價$B2$
  • $A1$等價$B2$,且$A2$等價$B1$

判定給定字串是否等價。
註 第二點的分割方法是指 $A=A1A2,B=B1B2$

解法:

感覺複雜度不大對,但是暴力做會AC,而且滿快的$(45ms)$,頗奇妙。
分析複雜度 $T(N) = 4T(\frac{N}{2})+2n$,主導者定理得到複雜度$O(N^2)$,但是大小$200000$還是過了,得找點資料研究下哪裡有問題。


#include<bits/stdc++.h>
using namespace std;

char A[200001];
char B[200001];
int L;

bool check(const char *a,const char *b,int len)
{
    if( strncmp(a,b,len) == 0 ) return true;
    if( len%2 == 1 )return false;
    len/=2;
    return check(a,b+len,len)&&check(a+len,b,len)||
           check(a,b,len)&&check(a+len,b+len,len);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>A>>B;
    L = strlen(A);
    cout<< (check(A,B,L)?"YES":"NO") <<'\n';
}
PS. 如果設定正確的話應該會出現在FB上

2015年8月12日 星期三

[Codeforces][559A][560C] A. Gerald's Hexagon

原題:http://codeforces.com/contest/559/problem/A
AC:http://codeforces.com/contest/559/submission/12473071

解題方向:數學


題目大意:有一個六邊形,皆為整數,且內角皆為$120^{\circ}$,問這圖形需要幾個邊長為$1$的正三角形組成 (一定給合法數值)。

解法:

對於旁邊的平行四邊形是$2$倍的邊長乘積,中間空下的正三角形邊長$P= \left |A-D  \right | = \left |B-E  \right | = \left |C-F \right | $ 就隨便選一個,所需要的三角形個數為$P^2$,全部加起來就好了。


#include<bits/stdc++.h>
using namespace std;

int main()
{
    int A,B,C,D,E,F;
    cin>>A>>B>>C>>D>>E>>F;
    cout<< 2*(A*B+C*D+E*F) + (A-D)*(A-D) <<'\n';
}

PS. 我覺得我好友的AC Code有夠醜 = =

2015年8月11日 星期二

[Codeforces][560B] B. Gerald is into Art

原題:http://codeforces.com/contest/560/problem/B
 AC:http://codeforces.com/contest/560/submission/12460603

解題方向:模擬

因為論壇壞了,把Code搬來這裡。

題目大意:有一個大矩形,問裡面能不能放入另外兩個矩形 (不能重疊,只考慮水平與垂直翻轉)

解法:可發現若兩矩形邊長為$A \times B$、$C \times D$,那最小包覆矩形至少要$(A+C,min(B,D))$,自己把ABCD排列組合一下,有任何一種狀況能滿足就可以了。




#include<bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int a[2],b[2],c[2];
    cin>>c[0]>>c[1];
    cin>>a[0]>>a[1]
       >>b[0]>>b[1];
    bool flag = false;
    for(int k=0;k<=1;++k)
        for(int i=0;i<=1;++i)
            for(int j=0;j<=1;++j)
                if( a[0^i]+b[0^j] <=c[0^k] && max(a[1^i],b[1^j]) <= c[1^k] )
                    flag = true;
    cout<< (flag?"YES":"NO") << '\n';
}