2017/8/7 离线赛

来源:互联网 发布:海文网络计划软件教程 编辑:程序博客网 时间:2024/05/22 04:56

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的整数部分。我们知道lg x=lg (a10b)=lg a+lg 10b=lg a+b,其中lg a一定为小数,所以我们只要取出lg x的小数部分即lg a就可以求出a了。又因为

lg (k=0[n1R]F1+kR)=k=0[n1R]lg F1+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 2000000000using 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存树上的fatherint 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的使用更谨慎了,以后切分的时候数组不敢乱开了。写题的时候要注意题目的性质,可以用来简化题目,不能掉进题目的坑里模拟。

原创粉丝点击