2015年1月31日 星期六

[CF 508E] Arthur and Brackets

這題我是寫DP解,聽說有O(n) 的greedy解,不過我還沒看@@ 感覺好神......

記 dp[ i ][ j ] 是代表第 i 個到第 j 個括號能不能組出一個合法的序列,如果不行就設 -1 ,可以的話就設第 i 個括號的右括號離左括號的距離,狀態有 O(n^2) 個,轉移 O(n)。

code :

#include<bits/stdc++.h>
using namespace std;
const int maxn=600+10 ;
 
int dp[maxn][maxn],l[maxn],r[maxn] ;
 
void print(int L,int R)
{
    if(L==R) { printf("()") ; return ; }
    if(L>R) return ;
    printf("(") ;
    print(L+1,L+(dp[L][R]-1)/2) ;
    printf(")") ;
    print(L+(dp[L][R]+1)/2,R) ;
}
 
main()
{
    int n ; scanf("%d",&n) ;
    for(int i=1;i<=n;i++) scanf("%d%d",&l[i],&r[i]) ;
    memset(dp,-1,sizeof(dp)) ;
    for(int i=n;i>=1;i--) for(int j=i;j<=n;j++)
    {
        if(i==j)
        {
            if(l[i]==1) dp[i][j]=1 ;
            continue ;
        }
        if(l[i]==1 && dp[i+1][j]!=-1) { dp[i][j]=1 ; continue ; }
        if(2*(j-i)+1>=l[i] && 2*(j-i)+1<=r[i] && dp[i+1][j]!=-1)
            { dp[i][j]=2*(j-i)+1 ; continue ; }
        for(int x=max(1,l[i]/2);j-i>x && 2*x+1<=r[i];x++)
            if(dp[i+1][i+x]!=-1 && dp[i+x+1][j]!=-1)
                { dp[i][j]=2*x+1 ; break ; }
    }
    if(dp[1][n]==-1) printf("IMPOSSIBLE") ;
    else print(1,n) ;
    printf("\n") ;
}
 

[CF 508D] Tanya and Password

傳統的尤拉路徑問題,如果字母是 xyz ,那就建 "xy" -> "yz" 的邊。
不過隔太久沒寫就不小心把他寫成O(E^2)了QQ

code :

#include<bits/stdc++.h>
using namespace std;
const int maxn=4000,maxm=200000+100 ;
 
int id(char c)
{
    if(c>='0' && c<='9') return 52+c-'0' ;
    if(c>='A' && c<='Z') return 26+c-'A' ;
    return c-'a' ;
}
 
int id(char *s)
{
    return 62*id(s[0])+id(s[1]) ;
}
 
char inv(int x)
{
    if(x>=52) return x-52+'0' ;
    if(x>=26) return x-26+'A' ;
    return x+'a' ;
}
 
struct P{int from,to;};
vector<P> edge ;
vector<int> v[maxn] ;
 
int in[maxn],out[maxn] ;
bool check()
{
    int cnt=0 ;
    for(int i=0;i<maxn;i++)
    {
        if(in[i]>out[i]+1 || out[i]>in[i]+1) return 0 ;
        if(in[i]!=out[i]) cnt++ ;
    }
    return (cnt<=2) ;
}
 
vector<int> ans ;
bool vis[maxm] ;
int now[maxn] ;
void dfs(int x)
{
    for(int i=now[x];i<v[x].size();i=now[x]+1)
    {
        now[x]++ ;
        if(!vis[v[x][i]]) vis[v[x][i]]=1 , dfs(edge[v[x][i]].to) ;
    }
    ans.push_back(x) ;
}
 
main()
{
    int n ; scanf("%d",&n) ;
    for(int i=0;i<n;i++)
    {
        char t[5] ; scanf("%s",t) ;
        int x=id(t) , y=id(t+1) ;
        edge.push_back((P){x,y}) ;
        in[y]++ ; out[x]++ ;
        v[x].push_back(i) ;
    }
 
    if(!check()) { printf("NO\n") ; return 0 ; }
    int st,odd=0 ;
    for(int i=0;i<maxn;i++) if(in[i]!=out[i]) odd=1 ;
    if(!odd) for(st=0;in[st]==0 && out[st]==0;st++) ;
    else for(st=0;out[st]!=in[st]+1;st++) ;
 
    dfs(st) ;
    if(ans.size()!=n+1) { printf("NO\n") ; return 0 ; }
    printf("YES\n") ;
    printf("%c",inv(ans[n]/62)) ;
    for(int i=n;i>=0;i--) printf("%c",inv(ans[i]%62)) ;
    printf("\n") ;
}
 

[CF] Round #289

一個 div 2 限定的比賽,說規則是仿ACM的,所以可以直接看到自己這題對不對,沒有hack。
總之就隨便寫寫......只寫了 A B D E,F 還沒想,可能之後會補上,至於 C 我覺得好麻煩就怒跳過了XD

A :


#include<bits/stdc++.h>
using namespace std;
int d[20][20] ;
main()
{
    int n ; scanf("%d",&n) ;
    for(int i=1;i<=n;i++) d[i][1]=d[1][i]=1 ;
    for(int i=2;i<=n;i++) for(int j=2;j<=n;j++)
        d[i][j]=d[i-1][j]+d[i][j-1] ;
    printf("%d\n",d[n][n]) ;
}
 

B :

#include<bits/stdc++.h>
using namespace std;
int a[200],b[200] ;
main()
{
    int n,k ; scanf("%d%d",&n,&k) ;
    int M=-1,m=101 ;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]) , m=min(m,a[i]) , M=max(M,a[i]) ;
    if(M-m > k) { printf("NO\n") ; return 0 ; }
    printf("YES\n") ;
    int q=m/k , r=m-q*k ;
    for(int i=1;i<=k;i++) b[i]=i<=r ? q+1 : q ;
    for(int i=1;i<=n;i++)
    {
        int d=a[i]-m ;
        for(int j=1;j<=k;j++)
        {
            int tm= d>0 ? b[j]+1 : b[j] ; d-- ;
            while(tm--) printf("%d ",j) ;
        }
        printf("\n") ;
    }
}
 

D :

#include<bits/stdc++.h>
#define MAX 100000000007
#define LL long long
using namespace std;
 
LL a[200][200] ;
set<LL> st ;
main()
{
    int n,m ; scanf("%d%d",&n,&m) ;
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
        scanf("%I64d",&a[i][j]) ;
    if(n==1)
    {
        printf("YES\n%I64d\n",MAX) ;
        printf("0\n") ;
        for(int i=1;i<=m;i++) printf("%I64d%c",a[1][i],i==m?'\n':' ') ;
        return 0 ;
    }
    if(m==1)
    {
        printf("YES\n%I64d\n",MAX) ;
        for(int i=1;i<=n;i++) printf("%I64d%c",a[i][1],i==n?'\n':' ') ;
        printf("0\n") ;
        return 0 ;
    }
 
    for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++)
        for(int k=1;k<=m;k++) for(int l=k+1;l<=m;l++)
        st.insert(a[i][k]+a[j][l]-a[i][l]-a[j][k]) ;
 
    LL now=*st.begin() ;
    for(auto i : st) now=__gcd(now,i) ;
    if(now<0) now=-now ;
 
    if(now)
    {
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
            if(a[i][j]>=now)
                { printf("NO\n") ; return 0 ; }
    }
 
    LL ans ;
    if(!now) ans=MAX ;
    else ans=now ;
 
    printf("YES\n%I64d\n",ans) ;
    for(int i=1;i<=n;i++) printf("%I64d%c",a[i][1],i==n?'\n':' ') ;
    for(int i=1;i<=m;i++)
        printf("%I64d%c",((a[1][i]-a[1][1])%ans+ans)%ans,i==m?'\n':' ') ;
}
 

E :

#include<bits/stdc++.h>
#define LL long long
#define DB double
using namespace std;
const int maxn=500000+100 ;
 
char s[maxn] ;
int n,a[maxn] ;
LL sum1[maxn],sum2[maxn],sum[maxn] ;
main()
{
    scanf("%s",s+1) ;
    set<char> st={'A','E','I','O','U','Y'} ;
    n=strlen(s+1) ;
    for(int i=1;i<=n;i++)
        a[i]= st.count(s[i]) ? 1 : 0 ;
 
    sum[0]=sum1[0]=0LL ;
    for(int i=1;i<=n;i++)
        sum1[i]=sum1[i-1]+i*a[i] ,
        sum[i]=sum[i-1]+a[i] ;
    sum2[n+1]=0LL ;
    for(int i=n;i>=1;i--) sum2[i]=sum2[i+1]+(n+1-i)*a[i] ;
 
    DB ans=0.0 ;
    for(int len=1;len<=n;len++)
    {
        LL add=0LL ;
        if(n>=2*len-1)
        {
            add+=sum1[len-1] ;
            add+=sum2[n-len+2] ;
            add+=(sum[n-len+1]-sum[len-1])*len ;
        }
        else
        {
            int t=n-len ;
            add+=sum1[t] ;
            add+=sum2[n-t+1] ;
            add+=(sum[n-t]-sum[t])*(n-len+1) ;
        }
        DB add2= ((DB)add)/((DB)len) ;
        ans+=add2 ;
    }
    printf("%.10f\n",ans) ;
}
 

[HOJ 150][POI 17 Stage 1] 鐵路

恩...這題我寫完AC了之後在證明作法的時候才發現我錯了OAO...我的作法有反例。

不過思路大概就是,先回去推原本只有一個軌道的那題的比較好看的充要條件,可以得到條件是:不存在三個數 i < j < k,滿足 a_j > a_i > a_k ,所以如果有這種情況出現在兩個軌道的情形的話, i 和 j 就必須要被放在不同的軌道中。於是我直接猜:兩個軌道的充要條件就是不存在四個數 i < j < k < l ,滿足 a_k > a_j > a_i > a_l (但這是錯的)。

如果考慮一張圖,頂點是1~n,然後如果有「 i 和 j 必須要被放在不同的軌道」這個限制就在 i , j 之間連一條邊,問題就轉化為判斷這張圖是不是二部圖。

但如果直接把圖建出來記憶體會不夠(也會TLE),因為這樣的邊數會到O(n^2),舉個簡單的測資: m+1 , m+2 , ... , n , 2 , 3 , ... , m , 1 ,其中 m 在 n/2 附近。至於要如何減少邊,首先我們可以不用先把圖全部建出來再判他是不是二部圖,而是可以一條邊一條邊加,用 disjoint set作動態的加邊,一有矛盾就退出之類的。另一個觀察是注意到,如果 a_(i+1) < a_i ,那麼 i+1 在 1 ~ i 所連的邊都已經被 i 連過了,所以可以只考慮 i+1 往前連的其中一條邊即可,這樣就加速很多了。

但我在寫這題的時候,以為前面我說的那個錯的充要條件是對的,所以在直接用那個條件 check 這數列有解之後就篤定他是二部圖,所以在 disjoint set 那邊沒有再判二部圖, 而且是建完整張圖後再DFS求答案(有加那個 a_(i+1) > a_i 的優化 ),正解應該是上面那樣做才對......不過既然都過了,那就懶的重寫一次正解了(炸

附上會讓上面那個條件爛掉的測資 : (答案應該是 NIE )

14
2 5 4 1 10 7 3 9 6 13 12 8 14 11

code :

#include<bits/stdc++.h>
#define pii pair<int,int>
#define F first
#define S second
#define mkp(x,y) make_pair(x,y)
using namespace std;
const int maxn=100000+10 ;

int n,a[maxn],inv[maxn],mi[maxn] ;
set<int> sti ;
set<pii> st ;

void insert(const pii &p)
{
        st.insert(p) ;
        set<pii>::iterator it=st.find(p) ;
        bool keep=1 ;
        if(it!=st.begin())
        {
                it-- ;
                if(it->S > p.S) keep=0 ;
                it++ ;
        }
        if(!keep) { st.erase(it) ; return ; }

        while(1)
        {
                set<pii>::iterator it2=it ;
                it2++ ; if(it2==st.end()) break ;
                if(it2->S < p.S) st.erase(it2) ;
                else break ;
        }
}

bool check()
{
        for(int i=1;i<n;i++)
        {
                if(i==1) { sti.insert(a[1]) ; continue ; }
                if(a[i]<(*sti.begin())) { sti.insert(a[i]) ; continue ; }
                if(!st.empty() && (*st.begin()).F < a[i])
                {
                        set<pii>::iterator it=st.lower_bound(mkp(a[i],a[i])) ;
                        it-- ;
                        if(mi[i+1]<it->S) return 0 ;
                }
                set<int>::iterator it=sti.lower_bound(a[i]) ; it-- ;
                insert(mkp(a[i],*it)) ; /// a[i] > *it
                sti.insert(a[i]) ;
        }
        return 1 ;
}

int fa[maxn] ;
int getfa(int x)
{
        return fa[x]==x ? x : fa[x]=getfa(fa[x]) ;
}

vector<int> v[maxn] ;
void add_edge(int x,int y)
{
        if(getfa(x)==getfa(y)) return ;
        v[x].push_back(y) ;
        v[y].push_back(x) ;
        fa[getfa(x)]=getfa(y) ;
}

int ans[maxn] ;
void dfs(int x)
{
        for(auto i : v[x]) if(!ans[i])
                ans[i]=3-ans[x] , dfs(i) ;
}

main()
{
        scanf("%d",&n) ;
        for(int i=1;i<=n;i++)
                scanf("%d",&a[i]) , inv[a[i]]=i ;
        for(int i=n;i>=1;i--) mi[i]= (i==n?a[i]:min(mi[i+1],a[i])) ;

        if(!check()) { printf("NIE\n") ; return 0 ; }

        sti.clear() ;
        for(int i=1;i<=n;i++) fa[i]=i ;
        for(int i=1;i<n;i++)
        {
                sti.insert(a[i]) ;
                if(a[i]==(*sti.begin())) continue ;
                set<int>::iterator it=sti.lower_bound(a[i]) ; it-- ;

                while(*it > mi[i+1])
                {
                        add_edge(inv[*it],i) ;
                        if(a[i]<a[i-1]) break ;
                        if(it==sti.begin()) break ;
                        it-- ;
                }
        }

        for(int i=1;i<=n;i++) if(!ans[i])
                ans[i]=1 , dfs(i) ;

        printf("TAK\n") ;
        for(int i=1;i<=n;i++) printf("%d%c",ans[i]," \n"[i==n]) ;
}

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") ;
    }
}