顯示具有 NPSC 標籤的文章。 顯示所有文章
顯示具有 NPSC 標籤的文章。 顯示所有文章

2015年1月30日 星期五

[ZJ b384][NPSC 2014] H. H Game

這題是我看官方的解+問人才會的,這解太神啦!!

作法:

當然直接用傳統的無限背包問題建表查詢不會過。

注意到如果 p 可以被湊出來,那麼 p + ci , p + 2*ci , ... 都可以被湊出來,所以如果隨便選定一個 ci (以下叫他 C ),考慮 d[ x ] 是模 C 餘 x 的所有價格中,最小能被湊出來的數字,那麼只要詢問的價錢大於等於這個數字,且和 x 對 C 同餘,那他就可以被湊出來,反之則不行。所以如果有了 d[ i ] 這個陣列那就可以 O(1) 回答每次詢問。

這時就會有類似最短路的模型出現,考慮 d[ x ] 加上其他任意一種幣值 ci ,那麼
d[ (x+ci) % C ] 的候選人又多了一個 d[ x ] + ci ,所以可以看成每個節點 ( 0 ~ C-1 ) 都連出了 n 條邊,且 d[ 0 ] = 0 ,去求 0 到其他所有點的最短路。所以可以用Dijkstra 作,複雜度
O(NClog(NC)),至於 C 因為要取哪個 ci 都可以,所以取最小的 ci 可以加速(很多!!)。

另外還有優化的方法,如果考慮的是: d2[ x ] 為滿足 C * d2[x] + x 是可以被湊出的,模 C = x 的最小價錢(也就等於 d[x] / C ) ( 在我的 code 裡面的 d 其實就是這裡的 d2 ),並且 C 取 ci 之中最大的,那每次鬆弛的時候 d2 最多會增加 1 ( 也就是如果 x 可以走到 y ,那麼這兩個數的 d2 至多差 1 ),所以用一個 deque 作最短路,遇到 d2 多1 的話就加到 deque 的後面,d2 一樣的話就加到 deque 的前面。這就類似傳統的BFS,只是因為BFS的時候每次都是固定增加1,所以擺在 queue 的最後就好,但這時有可能增加 0 ,所以需要一個 deque 把東西加在前面。複雜度降到O(NC)。

但這個優化會慢很多......主要是因為最大的C和最小的C差太多了,在我的電腦上用Dijkstra 作 + 取C是最小的 ci ,跑了30秒多,ZJ上跑了9.8秒,而用 deque 並取C是最大的 ci ,我的電腦跑了 2分半 ......至於把Dijkstra 改成取C是最大的 ci 也跑了3分鐘,比賽的時候如果寫出 deque 作法應該不會過OAO。

用Dijkstra 的 code :

#include<bits/stdc++.h>
#define LL long long
#define INF 100000000000LL
using namespace std;
const int maxn=1000000+10 ;
struct P
{
    int id ; LL val ;
    bool operator < (const P &rhs) const
    {
        return val>rhs.val ;
    }
};
 
priority_queue<P> pq ;
LL c[60] ;
LL d[maxn] ;
bool done[maxn] ;
main()
{
    int T ; scanf("%d",&T) ;
    while(T--)
    {
        int n,Q ; LL C=INF ;
        scanf("%d%d",&n,&Q) ;
        for(int i=1;i<=n;i++) scanf("%lld",&c[i]) , C=min(C,c[i]) ;
 
        for(int i=0;i<C;i++) d[i]=INF , done[i]=0 ;
        d[0]=0 ;
        while(!pq.empty()) pq.pop() ; pq.push((P){0,0}) ;
        while(!pq.empty())
        {
            P u=pq.top() ; pq.pop() ;
            if(done[u.id]) continue ;
            done[u.id]=1 ;
            for(int i=1;i<=n;i++)
            {
                LL val= u.id+C*d[u.id]+c[i] ;
                int id=val%C ;
                if(val/C < d[id])
                {
                    d[id]=val/C ;
                    pq.push((P){id,val/C}) ;
                }
            }
        }
 
        while(Q--)
        {
            LL p ; scanf("%lld",&p) ;
            if(d[p%C]==INF || (d[p%C]*C+(p%C) > p)) printf("N") ;
            else printf("Y") ;
        }
        printf("\n") ;
    }
}
 

用 deque 的 code :

#include<bits/stdc++.h>
#define LL long long
#define INF 100000000000LL
using namespace std;
const int maxn=1000000+10 ;
 
deque<int> dq ;
LL c[60] ;
LL d[maxn] ;
bool done[maxn] ;
main()
{
    int T ; scanf("%d",&T) ;
    while(T--)
    {
        int n,Q ; LL C=-1 ;
        scanf("%d%d",&n,&Q) ;
        for(int i=1;i<=n;i++) scanf("%lld",&c[i]) , C=max(C,c[i]) ;
 
        for(int i=0;i<C;i++) d[i]=INF , done[i]=0 ;
        d[0]=0LL ;
        while(!dq.empty()) dq.pop_front() ; dq.push_front(0) ;
        while(!dq.empty())
        {
            int u=dq.front() ; dq.pop_front() ;
            if(done[u]) continue ;
            done[u]=1 ;
            for(int i=1;i<=n;i++)
            {
                LL val=C*d[u]+u+c[i] ;
                int id=val%C ;
                if(d[id] > (val/C))
                {
                    d[id]=val/C ;
                    if(val/C > d[u]) dq.push_back(id) ;
                    else dq.push_front(id) ;
                }
            }
        }
        while(Q--)
        {
            LL p ; scanf("%lld",&p) ;
            if(d[p%C]==INF || (d[p%C]*C+(p%C) > p)) printf("N") ;
            else printf("Y") ;
        }
        printf("\n") ;
    }
}
 
 

[ZJ b383][NPSC 2014] F. 寧寧切蚯蚯

作法:參考NPSC官網的解...不解釋(?)

code :

#include<bits/stdc++.h>
using namespace std;
const int maxn=1600000+100 ;
 
char s[maxn],t[maxn] ;
 
int Z[maxn] ;
bool is_pal(int l,int r)
{
    l=2*l+1 ; r=2*r+1 ;
    return Z[(l+r)/2] + ((l+r)/2) >= r ;
}
 
int ext_len(int l,int r)
{
    int i=0 ;
    while(l-i>=0 && t[r+i]!='\0' && t[l-i]==t[r+i]) i++ ;
    return i-1 ;
}
 
void pre_cal()
{
    int len=strlen(s) ;
    for(int i=0;i<=2*len;i++) t[i]= (i%2==0)?'@':s[i/2] ;
    t[2*len+1]='\0' ; len=2*len+1 ;
 
    Z[0]=0 ;
    int L=0 , R=0 ;
    for(int i=1;i<len;i++)
    {
        if(i>R) Z[i]=ext_len(i,i) , L=i-Z[i] , R=i+Z[i] ;
        else
        {
            int i2=L+R-i ;
            if(i2-Z[i2] > L) Z[i]=Z[i2] ;
            else if(i2-Z[i2] == L)
            {
                Z[i]=R-i+ext_len(2*i-R,R) ;
                if(i+Z[i] > R) R=i+Z[i] , L=i-Z[i] ;
            }
            else Z[i]=R-i ;
        }
    }
}
 
bool is_two[2][maxn] ;
main()
{
    int T ; scanf("%d",&T) ;
    for(int tt=1;tt<=T;tt++)
    {
        memset(is_two,0,sizeof(is_two)) ;
        scanf("%s",s) ; if(tt==5) printf("%d\n",strlen(s)) ;
        int len=strlen(s) ;
        for(int i=2*len-1;i>=0;i--) s[i]=s[i/2] ;
        s[2*len]='\0' ; len*=2 ;
        pre_cal() ;
 
        int now=-1 ;
        for(int i=len-2;i>=0;i-=2)
        {
            if(now!=-1 && is_pal(i,now-1)) is_two[0][i]=1 ;
            if(is_pal(i,len-1)) now=i ;
        }
 
        now=-1 ;
        for(int i=1;i<len;i+=2)
        {
            if(now!=-1 && is_pal(now+1,i)) is_two[1][i]=1 ;
            if(is_pal(0,i)) now=i ;
        }
 
        bool ok=0 ;
        for(int i=1;i<=len-3;i+=2)
        {
            if(is_two[0][i+1] && is_pal(0,i))
                { printf("Yes\n") ; ok=1 ; break ; }
            if(is_two[1][i] && is_pal(i+1,len-1))
                { printf("Yes\n") ; ok=1 ; break ; }
        }
        if(!ok) printf("No\n") ;
    }
}
 

2015年1月29日 星期四

[ZJ b382][NPSC 2014] A. 曉涵的禮物

作法:

就暴力DFS,每個多方塊選一個位子放,還有他的旋轉情形,全部放完後更新最佳解。
但直接作會TLE,還要注意到如果有一個多方塊只有一格,那就不必要他的四種旋轉情形都試一次,所以預處理每個多方塊旋轉幾次會回到原本的,就可以剪枝了。比賽時我在DFS內才判斷這個多方塊轉回來了沒,所以一直吃TLE  QQ

官方解裡面還有另一種作法,就是對禮物盒子的高度作IDDFS,一遇到有解就馬上退出,因為如果這個高度已經有解了那麼更大的高度的最佳解一定是在後面放滿Z。

然後多方塊處理的問題還有UVa 1602,之前在算法競賽入門經典(第二版)裡看到這題,作者的code很簡潔易懂,無聊的話可以看看(?)

code :

#include<bits/stdc++.h>
using namespace std;
struct P
{
    int x,y ;
    bool operator < (const P &rhs) const
    {
        if(x!=rhs.x) return x<rhs.x ;
        else return y<rhs.y ;
    }
    bool operator != (const P &rhs) const { return (x!=rhs.x)||(y!=rhs.y) ; }
};
 
typedef vector<P> poly ;
vector<poly> v ;
char color[500] ;
 
bool equ(const poly &v1,const poly &v2)
{
    for(int i=0;i<v1.size();i++) if(v1[i]!=v2[i]) return 0 ;
    return 1 ;
}
 
void normalize(poly &p)
{
    int minx=1000 , miny=1000 ;
    for(auto i : p)
    {
        minx=min(minx,i.x) ;
        miny=min(miny,i.y) ;
    }
    poly q ;
    for(auto i : p) q.push_back((P){(i.x)-minx,(i.y)-miny}) ;
    p=q ;
}
 
void rotat(poly &p)
{
    poly q ;
    for(auto i : p) q.push_back((P){-i.y,i.x}) ;
    normalize(q) ;
    p=q ;
}
 
int n,m ;
char ans[500][500],now[500][500] ;
int rot_num[30] ;
 
bool better()
{
    for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(now[i][j]!=ans[i][j])
        return now[i][j]<ans[i][j] ;
    return 0 ;
}
 
bool can_place(int x,int y,const poly &p)
{
    for(auto i : p) if(now[x+i.x][y+i.y]!='Z') return 0 ;
    return 1 ;
}
 
 
void dfs(int x)
{
    if(x==(int)v.size())
    {
        if(better()) for(int i=0;i<n;i++) for(int j=0;j<m;j++)
            ans[i][j]=now[i][j] ;
        return ;
    }
    poly newp=v[x] ;
    for(int z=0;z<rot_num[x];z++)
    {
        int mx=0,my=0 ;
        for(auto it : newp) mx=max(mx,it.x) , my=max(my,it.y) ;
        for(int i=0;i<n;i++) for(int j=0;j<m;j++)
        {
            if(i+mx>=n || j+my>=m) continue ;
            if(can_place(i,j,newp))
            {
                for(auto it : newp) now[i+it.x][j+it.y]=color[x] ;
                dfs(x+1) ;
                for(auto it : newp) now[i+it.x][j+it.y]='Z' ;
            }
        }
        rotat(newp) ;
    }
}
 
main()
{
    int T ; scanf("%d",&T) ;
    while(T--)
    {
        int x ; scanf("%d%d%d",&n,&m,&x) ;
        v.clear() ;
        poly tmp ;
        for(int i=0;i<x;i++)
        {
            char s[100],ch ;
            tmp.clear() ;
            for(int j=0;j<4;j++)
            {
                scanf("%s",s) ;
                for(int k=0;k<4;k++) if(s[k]!='.')
                {
                    color[i]=s[k] ;
                    tmp.push_back((P){j,k}) ;
                }
            }
            normalize(tmp) ;
            v.push_back(tmp) ;
            if(i!=x-1) scanf("%s",s) ;
        }
 
        for(int i=0;i<x;i++)
        {
            poly tmp2=v[i] ;
            int num=0 ;
            for(num=1;;num++)
            {
                rotat(tmp2) ;
                sort(tmp2.begin(),tmp2.end()) ;
                if(equ(v[i],tmp2)) break ;
            }
            rot_num[i]=num ;
        }
 
        for(int i=0;i<n;i++) for(int j=0;j<m;j++) ans[i][j]=now[i][j]='Z' ;
        dfs(0) ;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(ans[i][j]=='Z') printf(".") ;
                else printf("%c",ans[i][j]) ;
            }
            printf("\n") ;
        }
        if(T) printf("#\n") ;
    }
}
 
 

[ZJ b379][NPSC 2014] D. 蚯蚯(扭)

剛好ZJ的解題數到30%了,NPSC 2014的題目又還沒傳到ZJ,所以就先試試看怎麼新增題目,把這年決賽的題目都加進去了。

這題我比賽時寫的作法是離線作,把每個操作記錄起來,然後每遇到一個詢問就循著這些操作往回推,輸出解。根據目前詢問的區間和被操作的區間會分成很多種可能,還要考慮現在這一段有沒有被反過來,還蠻麻煩的不過細心一點應該就OK了。

官方解是可持久化Treap,而他也是可持久化Treap的標準題型,之後可能有空(?)會補上code。關於可持久化Treap,可以看看:

随机平衡二叉查找树Treap 的分析与应用

可持久化平衡树

code :

#include<bits/stdc++.h>
using namespace std;
 
struct P{int tp,l,r ;};
vector<P> v ;
char s[20001] ;
 
void query(int x,int l,int r,int inv)
{
    //printf("\nquery %d %d %d %d\n",x,l,r,inv) ;
    if(l>r) return ;
    if(x==0)
    {
        if(!inv) for(int i=l;i<=r;i++) putchar(s[i]) ;
        else for(int i=r;i>=l;i--) putchar(s[i]) ;
        return ;
    }
    int tp=v[x-1].tp , L=v[x-1].l , R=v[x-1].r ;
    if(tp==2)
    {
        if(l>R) query(x-1,l-(R-L+1),r-(R-L+1),inv) ;
        else if(r<=R) query(x-1,l,r,inv) ;
        else if(!inv)
        {
            query(x-1,l,R,0) ;
            query(x-1,R+1-(R-L+1),r-(R-L+1),0) ;
        }
        else
        {
            query(x-1,R+1-(R-L+1),r-(R-L+1),1) ;
            query(x-1,l,R,1) ;
        }
    }
    else if(tp==3)
    {
        if(r<L) query(x-1,l,r,inv) ;
        else if(l>R) query(x-1,l,r,inv) ;
        else if(l<L && r>=L && r<=R)
        {
            if(!inv)
            {
                query(x-1,l,L-1,0) ;
                query(x-1,L+R-r,R,1) ;
            }
            else
            {
                query(x-1,L+R-r,R,0) ;
                query(x-1,l,L-1,1) ;
            }
        }
        else if(l>=L && l<=R && r>R)
        {
            if(!inv)
            {
                query(x-1,L,L+R-l,1) ;
                query(x-1,R+1,r,0) ;
            }
            else
            {
                query(x-1,R+1,r,1) ;
                query(x-1,L,L+R-l,0) ;
            }
        }
        else if(l<L && r>R)
        {
            if(!inv)
            {
                query(x-1,l,L-1,0) ;
                query(x-1,L,R,1) ;
                query(x-1,R+1,r,0) ;
            }
            else
            {
                query(x-1,R+1,r,1) ;
                query(x-1,L,R,0) ;
                query(x-1,l,L-1,1) ;
            }
        }
        else if(l>=L && l<=R && r>=L && r<=R)
        {
            if(!inv) query(x-1,L+R-r,L+R-l,1) ;
            else query(x-1,L+R-r,L+R-l,0) ;
        }
    }
}
 
main()
{
    int T ; scanf("%d",&T) ;
    while(T--)
    {
        int n,m ; scanf("%d%d%s",&n,&m,s+1) ;
        v.clear() ; int cnt=0 ;
        for(int i=1;i<=m;i++)
        {
            int tp,l,r ; scanf("%d%d%d",&tp,&l,&r) ;
            if(tp==2 || tp==3) v.push_back((P){tp,l,r}) , cnt++ ;
            else
            {
                query(cnt,l,r,0) ;
                printf("\n") ;
            }
        }
    }
}