usaco 2017 US Open

A:Modern Art

给定一个n*n的网格,有n*n个颜色,每种颜色按一定顺序覆盖了一个矩形。给定末状态,求有几种颜色可能是第一个填上去的。n<=1000

题解:二维查分+前缀和起来,然后就可以快速求得每个点被覆盖了多少次啦。复杂度n^2

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}

int n,ans=0;
int x1[1005],x2[1005],y1[1005],y2[1005];
int f[1005][1005],s[1005][1005];
bool b[1005];

int main()
{
    freopen("art.in","r",stdin);freopen("art.out","w",stdout);
    n=read();
    for(int i=1;i<=n*n;i++)x2[i]=y2[i]=0,x1[i]=y1[i]=1005;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            int x=read();s[i][j]=x;if(!x)continue;
            if(!b[x])ans++,b[x]=1;x1[x]=min(x1[x],i);x2[x]=max(x2[x],i);
            y1[x]=min(y1[x],j);y2[x]=max(y2[x],j);
        }
    if(ans==1)return 0*printf("%d",n*n-1);ans=n*n;
    for(int i=1;i<=n*n;i++)if(b[i])
    {
        f[x1[i]][y1[i]]++;f[++x2[i]][y1[i]]--;
        f[x1[i]][++y2[i]]--;f[x2[i]][y2[i]]++;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)f[i][j]+=f[i-1][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)f[i][j]+=f[i][j-1];

    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(s[i][j]&&b[s[i][j]])
            {
                int x=s[i][j];
                if(f[i][j]>1) b[x]=0,ans--;
            }
    cout<<ans;
    return 0;
}

B.Switch Grass

给定一个n个点m条边的图,每个点有一个颜色,边有边权。q个操作,每次修改一个点的颜色,询问连接的两个点颜色不同的权值最小的边的权值。n,m,q<=200000

先提供一种玄学做法,最坏n^2,但是usaco数据弱,也A了。

我们先把边按照权值排序,然后用fail[i]表示前i条边都不合法的情况下,下一条颜色可能不同的边是哪条。这个可以通过并查集处理出来。然后记录每个点最先出现的边的编号。每次修改,看一下修改的点的编号是否比答案小,如果小,那条边一定成为答案。如果不小的话,看一下是否把现在的答案这条边改成了相同的颜色,如果相同就沿着fail指针找到第一个不同的点,复杂度最坏n^2,但是由于数据弱,并且很多操作是O(1)的,所以卡过了,最久的点只跑了1074ms。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 200000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}

int n,m,k,q,ans;
int fail[MAXN+5];
int s[MAXN+5],col[MAXN+5],first[MAXN+5];
struct edge{
    int to,from,w,num;
}e[MAXN+5];
bool cmp(edge x,edge y){return x.w<y.w;}
int getfa(int x){return s[x]==x?x:s[x]=getfa(s[x]);}
bool combine(int x,int y){x=getfa(x);y=getfa(y);if(x!=y)s[x]=y;}
bool check(int x,int y){return getfa(x)==getfa(y);}

int main()
{
    freopen("grass.in","r",stdin);freopen("grass.out","w",stdout);
    n=read();m=read();k=read();q=read();
    for(register int i=1;i<=m;i++)
    {e[i].from=read();e[i].to=read();e[i].w=read();e[i].num=i;}
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++)s[i]=i;
    for(register int i=1,j=2;i<=m;i++)
    {
        combine(e[i].from,e[i].to);
        for(;j<=m&&check(e[j].from,e[j].to);)++j;
        fail[i]=j;
    }memset(first,127,sizeof(first));
    for(register int i=1;i<=n;i++)col[i]=read();
    for(register int i=1;i<=m;i++){first[e[i].to]=min(first[e[i].to],i);first[e[i].from]=min(first[e[i].from],i);}
    for(register int i=1;i<=m;i++)
    if(col[e[i].from]!=col[e[i].to]){ans=i;break;}
    for(register int i=1;i<=q;i++)
    {
        int x=read(),y=read();col[x]=y;
        if(first[x]<ans)ans=first[x];
        else while(col[e[ans].from]==col[e[ans].to])ans=fail[ans];
        printf("%d\n",e[ans].w);
    }
    return 0;
}

官方题解:我们可以发现对答案有贡献的边只会在最小生成树上,所以先跑出mst然后给他定一个根。然后我们对每一个点,对它儿子所有可能的颜色都建一个堆。

这样修改一个点只要从它父亲那里删掉一个点然后重新扔一个进去就可以了。这样每个点的答案就是和它颜色不同的堆的最小值,之后我们再对整体建一个堆,把每个

堆的答案扔进去,就可以啦。

我觉得堆有点难受,所以对每个节点建了个平衡树,还作死写了splay,之后再用线段树搞起来,差点T了.....复杂度nlogn 跑得比暴力还慢...

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 1500000
#define MAXL 200000
#define N 262144
#define INF 2000000000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
int n,m,qq,cnt=0;
int fa[MAXN+5],fat[MAXL+5],c[MAXN+5][2],s[MAXN+5],head[MAXL+5],ans[MAXN+5];
int rt[MAXL+5],col[MAXN+5],num[MAXN+5],mx[MAXN+5],size[MAXN+5],id[MAXN+5],upx[MAXN+5];
int T[N*2+5];
struct edge{
    int from,to,w;
}e[200005];
struct edge_type_2{
    int next,to,w;
}e2[400005];

bool cmp(edge x,edge y){return x.w<y.w;}
inline int getfa(int x){return !s[x]?x:s[x]=getfa(s[x]);}

void add(edge x)
{
    int f=x.from,t=x.to,w=x.w;
    e2[++cnt].next=head[f];e2[cnt].to=t;
    head[f]=cnt;e2[cnt].w=w;
    e2[++cnt].next=head[t];e2[cnt].to=f;
    head[t]=cnt;e2[cnt].w=w;
}

inline void mst()
{
    int x,y;
    for(register int i=1;i<=m;i++)
    {
        x=getfa(e[i].from);y=getfa(e[i].to);
        if(x!=y)s[x]=y,add(e[i]);
    }
}

void renew(int x,int ad)
{
   // cout<<"renew"<<x<<" "<<ad<<endl;
    T[x+=N]=ad;
    for(x>>=1;x;x>>=1)T[x]=min(T[x<<1],T[x<<1|1]);
}

inline void update(int x)
{
    if(!x)return;
    int l=c[x][0],r=c[x][1];
    size[x]=size[l]+size[r]+1;
    mx[x]=min(mx[l],min(mx[r],num[x]));
}

void rotate(int x,int&k)
{
    int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
    if(y==k)k=x;
    else c[z][c[z][1]==y]=x;
    fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
    c[y][l]=c[x][r];c[x][r]=y;
    update(y);update(x);
}

void splay(int x,int&k)
{
  //  cout<<"splay"<<x<<endl;
    while(x!=k)
    {
        int y=fa[x],z=fa[y];
        if(y!=k)
        {
            if(c[z][1]==y^c[y][1]==x)rotate(x,k);
            else rotate(y,k);
        }
        rotate(x,k);
    }
}

void ins(int&x,int rk,int last,int w,int fid)
{
  //  cout<<"ins"<<x<<" "<<rk<<" "<<last<<" "<<w<<endl;
    if(!x){x=++cnt;fa[x]=last;s[x]=rk;num[x]=mx[x]=w;size[x]=1;id[x]=fid;return;}
    if(s[x]==rk)ins(c[x][(fid>id[x])],rk,x,w,fid);
    else if(s[x]>rk)ins(c[x][0],rk,x,w,fid);
    else ins(c[x][1],rk,x,w,fid);
    update(x);
}

void dfs(int x,int f)
{
    fat[x]=f;
    for(int i=head[x];i;i=e2[i].next)
    if(e2[i].to!=f)
    {upx[e2[i].to]=e2[i].w;ins(rt[x],col[e2[i].to],0,e2[i].w,e2[i].to);dfs(e2[i].to,x);}
}

int find(int x,int rk,int fid)
{
    if(s[x]==rk)
    {if(id[x]==fid)return x;return find(c[x][(fid>id[x])],rk,fid);}
    if(s[x]>rk)return find(c[x][0],rk,fid);
    else return find(c[x][1],rk,fid);
}

int get(int x,int rk)
{
   // cout<<"get"<<x<<" "<<rk<<endl;
    int l=c[x][0],r=c[x][1];
    if(size[l]+1==rk)return x;
    else if(size[l]+1<rk)return get(r,rk-size[l]-1);
    else return get(l,rk);
}

int del(int x)
{
  //  cout<<"del"<<x<<endl;
    int y=fat[x];
    splay(find(rt[y],col[x],x),rt[y]);
    int w=num[rt[y]],rk=size[c[rt[y]][0]];
    splay(get(rt[y],rk),c[rt[y]][0]);
    int pre=c[rt[y]][0];
    c[pre][1]=c[rt[y]][1];
    fa[c[rt[y]][1]]=pre;rt[y]=pre;update(pre);
    return w;
}

int ask(int x,int num,int pos)
{
 //   cout<<"ask"<<x<<" "<<num<<" "<<pos<<" "<<s[x]<<endl;
    if(!x)return 0;int q;
    if(s[x]==num)return (q=ask(c[x][pos],num,pos))>0?q:x;
    else if(s[x]<num)return ask(c[x][1],num,pos);
    else return ask(c[x][0],num,pos);
}

int query(int x)
{
    int l=ask(rt[x],col[x],0),r=ask(rt[x],col[x],1);
    if(!l)return mx[rt[x]];
    splay(l,rt[x]);if(r==l)return min(mx[c[l][0]],mx[c[l][1]]);
    splay(r,c[l][1]);
    return min(mx[c[rt[x]][0]],mx[c[c[rt[x]][1]][1]]);
}

int main()
{
    freopen("grass.in","r",stdin);
    freopen("grass.out","w",stdout);
    n=read();m=read();read();qq=read();mx[0]=INF;
    for(register int i=1;i<=m;i++)
        e[i].from=read(),e[i].to=read(),e[i].w=read();
    for(register int i=1;i<=n;++i)col[i]=read();
    sort(e+1,e+m+1,cmp);
    mst();cnt=0;
    for(register int i=1;i<=n;++i)ins(rt[i],-INF,0,INF,0),ins(rt[i],INF,0,INF,0);
    dfs(1,0);
    for(register int i=1;i<=2*N+1;++i)T[i]=INF;
    for(register int i=1;i<=n;++i)renew(i,ans[i]=query(i));
    int x,c;
    for(register int i=1;i<=qq;++i)
    {
        x=read(),c=read();
        if(col[x]!=c)
        {
            if(x!=1){ins(rt[fat[x]],c,0,del(x),x);
                if(upx[x]<ans[fat[x]]||c==col[fat[x]])renew(fat[x],ans[fat[x]]=query(fat[x]));}
            col[x]=c;renew(x,ans[x]=query(x));
        }
        printf("%d\n",T[1]);
    }
    return 0;
}

C沙比提不想做

时间: 2024-10-27 06:48:39

usaco 2017 US Open的相关文章

USACO 2017 February Platinum

第二次参加USACO 本来打算2016-2017全勤的 January的好像忘记打了 听群里有人讨论才想起来铂金组三题很有意思,都是两个排列的交叉对问题 我最后得分889/1000(真的菜)先发下当时做的,A的之后补 T1.Why Did the Cow Cross the Road题目大意:给出两个N个排列(N<=100,000),允许把其中一个排列循环移动任意位,a[i]表示i在第一个排列中的位置,b[i]表示第二个,定义交叉对(i,j)满足a[i]<a[j]且b[i]>b[j],求

USACO 2017 FEB Gold visitfj 最短路

题意 有一幅n*n的方格图,n <= 100,每个点上有一个值.从(1,1)出发,走到(n,n),只能走四联通.每走一步花费t,每走三步需要花费走完三步后到达格子的值.求最小花费的值. 拆点,dis[i][j]表示到达第i个点时走的总步数模3等于j时的最小花费值. #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorith

USACO 2017 FEB Platinum nocross DP

题目大意 上下有两个长度为n.位置对应的序列A.B,其中数的范围均为1~n.若abs(A[i]-B[j]) <= 4,则A[i]与B[j]间可以连一条边.现要求在边与边不相交的情况下的最大的连边数量.n <= 10^5. 在Gold里,此题的数据范围是1000,我们完全可以用简单的最长公共连续子序列的DP方法来做. 范围大了之后,可以观察到对于一个数A[i],它所能转移的状态最多只有9个,那么就可以顺序扫描A数组,设F[i][j]表示当前连得最后一条边为(A[i],B[to[i][j]])的最

USACO 2017 January Platinum

因为之前忘做了,赶紧补上. T1.Promotion Counting 题目大意:给定一个以1为根的N个节点的树(N<=100,000),每个节点有一个权值,对于每个节点求出权值比它大的子孙的个数. 思路:肯定先要求出dfs序,首先无脑想到主席树,后来发现只要按权值从大到小处理就不用那么麻烦了. #include<cstdio> #include<algorithm> using namespace std; char B[1<<26],*S=B,C;int X;

USACO 2017 December Contest Gold T1: A Pie for a Pie

题目大意 Bessie和Elsie各自烤了 N(1≤N≤10^5)个馅饼.Bessie 会这 2N 个馅饼打分,Elsie 也会.二者的打分均为一个 ≤1e9 的非负整数.由于她们口味不同,每个派的两个分数可能不同.她们想互赠礼物.开始时,Bessie 送给 Elsie 一个馅饼.她们收到礼物(对方做的馅饼)后都会回赠对方一个自己做的馅饼.她们选择回礼的方法相同.以 Elsie 为例,Elsie 根据自己的打分来选择回礼.回礼的分数至少要大于她收到的馅饼的分数,但两个馅饼的分数差不能大于 D(0

USACO 2017 December Contest Platinum T2: Push a Box

题目大意 一个谷仓是一个N*M的矩形网格,有一些网格里有干草.Bessie站在其中一个格子内,还有一个格子里有一个大木箱.Bessie不能和大木箱在一个格子里,也不能和干草在一个格子里. 如果她不与干草一个格子,她就可以往自己旁边的四个方向(东西南北)移动,如果她想移动到有木箱的格子里,那个木箱就会被她推一格(只要木箱的那个方向还有空间),如果没有空间,那Bessie就不能移动了. 给你谷仓的布局(空格子,干草以及木箱位置)以及Bessie的出发位置和箱子要被推到的位置,请你帮忙计算Bessie

USACO 2017 December Contest Platinum T3: Greedy Gift Takers

题目大意 有 N(1≤N≤1e5)头牛按顺序排成一列,编号从 1 到 N,1 号牛在队头,N 号牛在队尾. 每次位于队头的牛 i 拿到一个礼物,然后插入到从队尾数ci?头牛之前的位置..举个栗子: 初始队列 1,2,3,4,5 c1?= 2,c2? = 3,则第一次操作后的序列为 2,3,1,4,5,第二次操作后的序列为 3,2,1,4,5.重复无限次操作,求最后有几头牛拿不到礼物. 题目分析 一上来有个显然的结论,若一个人得不到礼物那么原序列中在他后面的人肯定也得不到礼物,因为后面的人跳不到前

中斯间极积况意称天参并

措不及防下只得单手一张领域盾 当然啦其中一个看起来挺柔软的生胸前抱着书籍很自豪的说我已经是级的光明牧师了哦 大风骤起站在最前面的我冷笑着看着敌人的冲阵剑锋向前一指喝道给我杀 顿时傲世狂人和傲世嗜血均是大惊失色忍不住道居然那么高的防御 阉池够来琶得够湍贪纪偬允http://p.baidu.com/ihome/center?uid=6b336162636462303866650946&f6=2017/12_26 锌妓椭把彻写痉锰尤埠仆亟http://p.baidu.com/ihome/center?

平向图问济须提标省子离

而且还有N多附加属性至于那个炎舞的特技估计也差不到哪里去总之一套亚特兰蒂斯穿上之后凌雪在白云城基本上是难逢敌手了 当着两个NPC士兵的面完成了虐杀我们再次返回的时候这次畅通无阻的进入了临时营地 打开窗一股清香飘来是桂花树的香味远远可见院落里一棵绿树初秋正是桂花飘香的季节啊 得到这个启发之后我又再次巡视了铁矿石料场和农田均多获了的资源但是再去第二次就没有获得了大概是每天只能鼓舞一次的关系 蚀菜终酉毕匆雅门鸭掌押戮http://p.baidu.com/ihome/center?uid=1865616