1. 程式人生 > >2017/8/7 離線賽

2017/8/7 離線賽

T1 Codechef JAN17 務農政策

    這題的解法很好想,就是每次詢問時列舉每個矩形,然後求出最小的花費, 每個矩形的終態就是這個矩形中最高的那塊,我們只要用二維字首和維護每個矩形的權值和,再求出每個矩形中最大的權值即可。難點在怎麼求每個矩形的最大權值,這是一個二維的部分最大值。由於之前寫了球染色這道題,所以腦子裡印象比較深刻,我們立馬想用單調佇列求出這個最大值,但是發現二維的不好維護,我就先維護了每一行的最大值,這樣把一段區間加在一個點上,再豎著來一遍,就求出二維的最大值了。

typedef pair<int,int> P;
int n,m,mi=2e9,mx;
int
h[M][M]; ll sum[M][M]; int Max[M][M]; P que[M]; int L,R; int T; int main(){ int a,b; Rd(n);Rd(m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ Rd(h[i][j]); mi=min(mi,h[i][j]); mx=max(mx,h[i][j]); sum[i][j]=sum[i-1][j-1]+sum[i][j-1]-sum[i-1][j-1]+sum
[i-1][j]-sum[i-1][j-1]+h[i][j]; } Rd(T); while(T--){ memset(Max,0,sizeof(Max)); ll ans=2e18; Rd(a);Rd(b); for(int i=1;i<=n;i++){ L=R=0; for(int j=1;j<=m;j++){ while(L<R&&que[R-1].first<h[i][j])R--; que[R++]=P(h[i][j],j); while
(L<R&&j-que[L].second>=b)L++; Max[i][j]=que[L].first; } } for(int j=b;j<=m;j++){ L=R=0; for(int i=1;i<=n;i++){ while(L<R&&que[R-1].first<Max[i][j])R--; que[R++]=P(Max[i][j],i); while(L<R&&i-que[L].second>=a)L++; Max[i][j]=que[L].first; } } for(int i=a;i<=n;i++) for(int j=b;j<=m;j++)//列舉右下角 ans=min(ans,1LL*a*b*Max[i][j]-(sum[i][j]-sum[i-a][j]-sum[i][j-b]+sum[i-a][j-b])); Pt(ans); putchar('\n'); } return 0; }

T2 Codechef JUNE16 刷副本

    這道題的難點就在如何求首位了,考試的時候我敲了個高精,然後手賤陣列開了50000,每次定義一次要memset一次,然後就炸了,本來有65分的()是我太相信memset了【捂臉】)。對於最終答案x,用科學計數法可表示為x=a10b,a[0,10),那麼可以知道這個數字的首位為a的整數部分。我們知道lgx=lg(a10b)=lga+lg10b=lga+b,其中lga一定為小數,所以我們只要取出lgx的小數部分即lga就可以求出a了。又因為lg(k=0[n1R]F1+kR)=k=0[n1R]lgF1+kR

    我們只要處理出每個數的lg值,然後把需要的lg值相加就是最終結果的lg值了,取出最終結果的小數部分i,那麼首位就是10i的整數部分了。

    接下來是查詢和更新操作了。對於查詢,我們事先預處理出每種R的答案,查詢時就是O(1)的。對於更新,我們把x的因子的答案都修改一遍,由於當R=1時,每次都會被更新到,這樣很慢,我們要把1拉出來單獨更新。

int fast(int a,int p){
    int res=1;
    while(p){
        if(p&1)res=1LL*res*a%P;
        a=1LL*a*a%P;
        p>>=1;
    }
    return res;
}
int n,q;
int A[M];
int fac[M];
double sum[M];
vector<int>son[M];
int main(){
    int opr,x,f;
    Rd(n);
    for(int i=0;i<n;i++)Rd(A[i]);
    for(int i=1;i<=n;i++){
        fac[i]=1;
        for(int j=i;j<n;j+=i){
            fac[i]=1LL*fac[i]*A[j]%P;
            sum[i]+=log10(A[j]);
            son[j].push_back(i);
        }
    }
    Rd(q);
    while(q--){
        Rd(opr);
        if(opr==1){
            Rd(x);Rd(f);x--;
            if(x){
                int pre=A[x];
                double t=log10(pre);
                int tt=fast(pre,P-2);
                double ttt=log10(f);
                for(int i=0;i<son[x].size();i++){
                    int v=son[x][i];
                    sum[v]-=t;
                    sum[v]+=ttt;
                    fac[v]=1LL*fac[v]*tt%P*f%P;
                }
            }
            A[x]=f; 
        }else{
            Rd(x);
            int res=1LL*fac[x]*A[0]%P;
            double fi=sum[x]+log10(A[0]);
            int ans=(int)pow(10,fi-floor(fi)+eps);
            if(ans==10)ans=1;
            Pt(ans);
            putchar(' ');
            Pt(res);
            putchar('\n');
        }
    }
    return 0;
}

T3 USACO 2017 Open Platinum 聖盃戰爭

    唉,這題考試時不知道怎麼想的,敲了個Dijkstra水了20分。絲毫沒有想到一個最簡單的性質:最終答案一定是一條邊,這是顯而易見的,假設有兩條邊組成了最終答案,那麼中間這個點肯定會和兩邊的點顏色不同,那麼答案就是中間的點和那個顏色不同的點的連線,這就違背之前的假設。再往後推,我們知道這條邊,一定是最小生成樹上的一條邊,因為最小的答案一定在最小生成樹上, 這符合Kruskal的造樹方式。

    發現了這個性質,題目就好想了,我們不妨以1為根,對每個點建樹,存下這個點到達每種顏色兒子的花費,由於記憶體開不下,我們可以採用動態造節點的方式,因為在最大資料中一個點能用到的兒子最多隻有10個。對於一個節點的貢獻,只要query(1,A[x]1)query(A[x]+1,K)即可,存答案可以用multiset

#include<set>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define M 200005
#define oo 2000000000
using namespace std;
template <class T>
inline void Rd(T &res){
    char c;res=0;int k=1;
    while(c=getchar(),c<48&&c!='-');
    if(c=='-'){k=-1;c='0';}
    do{
        res=(res<<3)+(res<<1)+(c^48);
    }while(c=getchar(),c>=48);
    res*=k;
}
template <class T>
inline void Pt(T res){
    if(res<0){
        putchar('\n');
        res=-res;
    }
    if(res>=10)Pt(res/10);
    putchar(res%10+48);
}
struct ED{
    int a,b,w;
    bool operator <(const ED &tmp)const{
        return w<tmp.w;
    }
}E[M<<1];
struct edge{
    int v,nxt,cost;
}e[M<<1];
multiset<int>ans,s[M<<2];
int n,m,K,Q;
int A[M],fa[M],pa[M],res[M],la[M];//pa存樹上的father
int rt[M],num[M*60],sz,lr[M*60],rr[M*60];
int id[M*60],ct;
int head[M],edgecnt;
int get_fa(int v){
    if(fa[v]==v)return v;
    return fa[v]=get_fa(fa[v]);
}
bool same(int u,int v){
    return get_fa(u)==get_fa(v);
}
void merge(int u,int v){
    int px=get_fa(u);
    int py=get_fa(v);
    fa[py]=px;
}
void add_edge(int u,int v,int cost){
    e[++edgecnt].v=v;e[edgecnt].cost=cost;e[edgecnt].nxt=head[u];head[u]=edgecnt;
}
void up(int p){
    num[p]=min(num[lr[p]],num[rr[p]]);
}
int query(int p,int L,int R,int l,int r){
    if(!p||l>r)return oo;
    if(l==L&&r==R)return num[p];
    int mid=(L+R)>>1;
    if(r<=mid)return query(lr[p],L,mid,l,r);
    if(l>mid)return query(rr[p],mid+1,R,l,r);
    return min(query(lr[p],L,mid,l,mid),query(rr[p],mid+1,R,mid+1,r));
}
void update(int &p,int L,int R,int x,int w,bool f){
    if(!p){p=++sz;num[p]=oo;}
    if(L==R){
        if(!id[p])id[p]=++ct;
        int now=id[p];
        if(f)s[now].insert(w);
        else s[now].erase(s[now].find(w));
        if(s[now].size())num[p]=*(s[now].begin());
        else num[p]=oo;
        return;
    }
    int mid=(L+R)>>1;
    if(x<=mid)update(lr[p],L,mid,x,w,f);
    else update(rr[p],mid+1,R,x,w,f);
    up(p);
}
void dfs(int x,int t){
    pa[x]=t;
    for(int i=head[x];~i;i=e[i].nxt){
        int v=e[i].v;
        if(v==t)continue;
        dfs(v,x);
        res[v]=e[i].cost;
        update(rt[x],1,K,A[v],e[i].cost,1);
    }
    if(rt[x]){
        la[x]=min(query(rt[x],1,K,1,A[x]-1),query(rt[x],1,K,A[x]+1,K));
        ans.insert(la[x]);
    }
}
void Init(){
    memset(head,-1,sizeof(head));
    memset(pa,-1,sizeof(pa));
    num[0]=oo;
    sort(E+1,E+m+1);
    for(register int i=1;i<=m;++i){
        int u=E[i].a,v=E[i].b;
        if(!same(u,v)){
            add_edge(u,v,E[i].w);
            add_edge(v,u,E[i].w);
            merge(u,v);
        }
    }
    dfs(1,-1);
}
int main(){
    int x,k;
    Rd(n);Rd(m);Rd(K);Rd(Q);
    for(int i=1;i<=m;i++){
        Rd(E[i].a);Rd(E[i].b);Rd(E[i].w);
    }
    for(int i=1;i<=n;i++){
        Rd(A[i]);
        fa[i]=i;
    }
    Init();
    while(Q--){
        Rd(x);Rd(k);
        if(k!=A[x]){
            if(rt[x]){
                ans.erase(ans.find(la[x]));
                la[x]=min(query(rt[x],1,K,1,k-1),query(rt[x],1,K,k+1,K));
                ans.insert(la[x]);
            }
            if(x!=1){
                int f=pa[x];
                ans.erase(ans.find(la[f]));
                update(rt[f],1,K,A[x],res[x],0);
                update(rt[f],1,K,k,res[x],1);
                la[f]=min(query(rt[f],1,K,1,A[f]-1),query(rt[f],1,K,A[f]+1,K));
                ans.insert(la[f]);
            }
            A[x]=k;
        }
        Pt(*(ans.begin()));
        putchar('\n');
    }
    return 0;
}

    T2的失算讓我對memset的使用更謹慎了,以後切分的時候陣列不敢亂開了。寫題的時候要注意題目的性質,可以用來簡化題目,不能掉進題目的坑裡模擬。