Codefoces310 div1

昨晚陪lxe大神组队随意找了一场div1来打
第一打div1非常激动,感觉题目一定会坑爹的不行
结果……

A题
n组套娃,保证所有套娃合起来是1-m,求最少的合并操作数
傻逼题。把包含1的那坨找出来,除了1,2,3…的以外,其它打开暴力合并

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
int n,m,k,ans;
int a[100005];
int main()
{   //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    int i,j,temp;
    scanf ("%d %d",&n,&k);
    for (i=1;i<=k;i++)
    {
        scanf ("%d",&m);
        for (j=1;j<=m;j++)
        scanf ("%d",&a[j]);
        sort(a+1,a+m+1);
        if (a[1]==1)
        {
            temp=2;
            while(temp<=m && a[temp]==a[temp-1]+1)temp++;temp--;
            m-=temp-1;
        }
        ans+=m-1;
    }
    ans+=n-temp;
    cout<<ans<<endl;
    return 0;
}

B题
n段数轴上的区间,m座桥,每座桥有一个长度,一座长度为a的桥能被用来连接i和i+1两个区间当且仅当存在x, y使得t li ≤ x ≤ ri, li + 1 ≤ y ≤ ri + 1 且 y - x =  a。求是否存在方案是所有i和i+1能连接(n,m<=200000)
普及组傻逼题我竟然不会做……真不知道自己在干吗
后来还是lxe提醒了一下……
显然,对于某个i,能用来连接它与i+1的桥的长度属于[li+1-ri,ri+1-li]。然后我们考虑每座桥用来连那一座,显然,如果左区间满足,肯定选右区间最小的那一个。从左到右扫一遍,拿个堆维护就好了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <deque>
#include <queue>
#include <map>
#include <set>
#include <ctime>
using namespace std;

typedef long long ll;
const int Maxn=200005;

int n,m;
ll ans[Maxn];
struct Segment
{
    ll l,r; int pos;
}A[Maxn],land[Maxn];
bool cmp(const Segment &a,const Segment &b) { return a.l<b.l || a.l==b.l && a.r<b.r; }

struct node
{
    int pos; ll val;
    node(){} node(int _pos,ll _val) { pos=_pos; val=_val; }
}p[Maxn];
bool operator < (node a,node b) { return a.val>b.val; }
bool cmp2(const node &a,const node &b) { return a.val<b.val; }
priority_queue<node> H;

int Get()
{
    char ch; int v=0; bool f=false;
    while (!isdigit(ch=getchar())) if (ch=='-') f=true; v=ch-48;
    while (isdigit(ch=getchar())) v=v*10+ch-48;
    if (f) return -v;else return v; 
}

int main()
{
    n=Get(),m=Get();
    for (int i=1;i<=n;i++) scanf("%I64d%I64d",&land[i].l,&land[i].r);
    for (int i=1;i<=m;i++) scanf("%I64d",&p[i].val),p[i].pos=i;
    for (int i=1;i<=n-1;i++) A[i].l=land[i+1].l-land[i].r,A[i].r=land[i+1].r-land[i].l,A[i].pos=i;
    n--;
    sort(A+1,A+1+n,cmp);
    sort(p+1,p+1+m,cmp2);

    if (m<n) { puts("No"); return 0; }
    ll mx=0;
    for (int i=1,j=1;i<=m;i++)
    {
        while (A[j].l<=p[i].val && j<=n) H.push(node(A[j].pos,A[j].r)),j++;
        while (!H.empty() && H.top().val<p[i].val) H.pop();
        if (!H.empty())
        {
            node tmp=H.top();
            ans[tmp.pos]=p[i].pos;
            H.pop();
        }
    }
    bool flag=true;
    for (int i=1;i<=n;i++) if (ans[i]==0) flag=false;
    if (flag)
    {
        puts("Yes");
        for (int i=1;i<=n-1;i++) printf("%d ",ans[i]);
        printf("%d\n",ans[n]);
    }
    else puts("No");
    return 0;
}

C题
给你一个腰长为n的等腰直角三角形,还有m个操作,每次要吗从底边往左边的腰上砍一刀,要吗往右边的腰上砍一刀,但是如果砍到之前的刀痕就停止。每次砍不会真正的砍下来,求每次砍下的面积。(n<=10^9,m<=100000)

傻逼线段树。发现左边的只有右边的有影响,且刚好是那个点上的最大值会有影响。所以左右各建一颗线段树,然后每次查询,把查询的出的区间再修改就好了。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
const int Maxn=200005;
int n,q,flag[Maxn],C[Maxn],D[Maxn],lc,ld;
int pla[Maxn],plb[Maxn];
char kind[Maxn];
struct point
{   int l,r,max;
};
struct SGT
{   point s[Maxn*6];
    void build(int x,int l,int r)
    {
        if (l>r) return;
        if (l==r)
        {
            s[x].l=l,s[x].r=r,s[x].max=0;
            return;
        }
        build(x*2,l,(l+r)/2);
        build(x*2+1,(l+r)/2+1,r);
        s[x].l=l,s[x].r=r,s[x].max=0;
    }
    void setmax(int x,int l,int r,int v)
    {
        if (l>r) return;
        if (s[x].l==l && s[x].r==r)
        {
            s[x].max=max(s[x].max,v);
            return;
        }
        if (r<=(s[x].l+s[x].r)/2) setmax(x*2,l,r,v);
        else if (l>(s[x].l+s[x].r)/2) setmax(x*2+1,l,r,v);
        else setmax(x*2,l,(s[x].l+s[x].r)/2,v),setmax(x*2+1,(s[x].l+s[x].r)/2+1,r,v);
    }
    int getmax(int x,int l)
    {
        if (s[x].l==s[x].r) return s[x].max;
        if (l>(s[x].l+s[x].r)/2) return max(s[x].max,getmax(x*2+1,l));
        return max(s[x].max,getmax(x*2,l));
    }
}T1,T2;//T1:-  T2:|
int main()
{   //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    int i,j,x,y,temp;
    char ch;
    scanf ("%d %d",&n,&q);
    for (i=1;i<=q;i++)
    {
        scanf ("%d %d %c",&x,&y,&ch);
        kind[i]=ch;
        pla[i]=x;
        plb[i]=y;
        C[i]=x;
        D[i]=y;
    }
    sort(C+1,C+q+1);
    lc=unique(C+1,C+q+1)-C-1;
    sort(D+1,D+q+1);
    ld=unique(D+1,D+q+1)-D-1;
    T1.build(1,1,lc);
    T2.build(1,1,ld);
    for (i=1;i<=q;i++)
    {
        pla[i]=lower_bound(C+1,C+lc+1,pla[i])-C;
        plb[i]=lower_bound(D+1,D+ld+1,plb[i])-D;
    }

    for (i=1;i<=q;i++)
    {
        x=pla[i],y=plb[i],ch=kind[i];
        if (flag[x]) cout<<"0"<<endl;
        else
        {
            flag[x]=1;
            if (ch=='U')
            {
                temp=T1.getmax(1,x);
                cout<<D[y]-D[temp]<<endl;
                T2.setmax(1,temp+1,y,x);
            }
            else
            {
                temp=T2.getmax(1,y);
                cout<<C[x]-C[temp]<<endl;
                T1.setmax(1,temp+1,x,y);
            }
        }
    }
    return 0;
}

D题:
数轴上有n个点,m个询问,每次询问从一个点开始,有一条长度为l的绳子,从这个点开始,绳子开始打转,打转到最远的那个点,就定住,剩下的长度接着转,求最后停在那(n,m<=200000)

傻逼模拟。lxe说可以证明每次暴力模拟,加点取模优化,次数不会很多(似乎是log2(n)级别的?)反正我不知道。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
const int Maxn=200005;
int n,q;
int a[Maxn],u[Maxn];
struct num
{   int x,p;
}s[Maxn];
int right(int x)
{   return lower_bound(a,a+n+2,x)-a;
}
int left(int x)
{   return upper_bound(a,a+n+2,x)-a-1;
}
bool cmp(num x,num y)
{   return x.x<y.x;
}
bool cmp2(num x,num y)
{   return x.p<y.p;
}
void work(int x,int y,int l)
{   if (x==y){cout<<u[x]<<endl;return;}
    int temp=l/(a[y]-a[x]),z;
    l%=a[y]-a[x];
    if (temp&1)
    {
        z=right(a[y]-l);
        work(z,y,l-(a[y]-a[z]));
    }
    else
    {
        z=left(a[x]+l);l-=a[z]-a[x];
        x=right(a[z]-l);l-=a[z]-a[x];
        work(x,z,l);
    }
}
int main()
{   //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    int i,j,x,l,y,z;
    scanf ("%d %d",&n,&q);
    for (i=1;i<=n;i++) scanf ("%d",&a[i]),s[i].x=a[i],s[i].p=i;
    sort(a+1,a+n+1);
    sort(s+1,s+n+1,cmp);
    for (i=1;i<=n;i++)
        s[i].x=i;
    sort(s+1,s+n+1,cmp2);//s:p->x
    for (i=1;i<=n;i++) u[s[i].x]=i;//x->p
    a[0]=-(2e9+7),a[n+1]=2e9+7;
    for (i=1;i<=q;i++)
    {
        scanf ("%d %d",&x,&l);
        x=s[x].x;
        y=left(a[x]+l);l-=a[y]-a[x];
        z=right(a[y]-l);l-=a[y]-a[z];
        work(z,y,l);
    }
    return 0;
}

E题:
给你一个n个点m条边的图,再给你若干K个起始点,终止点,你要规定每条边的方向,使得从每个起始点开始,可以走到对应的终止点。求是否存在这样的方案。(n,m,K<=200000)
傻逼tarjan缩点+树剖+线段树。显然对于一个双联通分量中的点,不管怎么定方向,都可以互相到达。然后用tarjan缩一发点,变成一棵树。那么显然,对于一条起始点为u,终止点为v的路径,可以使得,u->lca(u,v)的边变得朝向根的方向,lca(u,v)->v的边变得朝向根方向的相反方向,然后树剖+线段树维护一下,判断一下是否矛盾就好了。
反正这题我剩1h15min开始敲,各种复制模板,然后考后5min调完……236行……(写得我想艹)而且最坑爹的是我发现好像有更快的不用写树剖的方法。。。。。他妈的就我写了树剖,其它都是什么并查集之类的,反正我这种蒟蒻是不会。。。。。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <deque>
#include <queue>
#include <map>
#include <set>
#include <ctime>
using namespace std;

const int Maxn=200005;

int q,cnt,st[Maxn],ed[Maxn],belong[Maxn];

int Get()
{
    char ch; int v=0; bool f=false;
    while (!isdigit(ch=getchar())) if (ch=='-') f=true; v=ch-48;
    while (isdigit(ch=getchar())) v=v*10+ch-48;
    if (f) return -v;else return v; 
}

class Segment_Tree
{
    public:
    struct node { int L,R,lazy,val; }; node tr[Maxn<<2];

    void mark(int x,int v) { tr[x].lazy=v,tr[x].val=v; }
    void update(int x)
    {
        if (tr[x<<1].val==3 || tr[x<<1|1].val==3) { tr[x].val=3;return; }
        if (tr[x<<1].val==1)
        {
            if (tr[x<<1|1].val==2) tr[x].val=3;
            else tr[x].val=1;
        }
        if (tr[x<<1].val==2)
        {
            if (tr[x<<1|1].val==1) tr[x].val=3;
            else tr[x].val=2;
        }
        if (tr[x<<1].val==0) tr[x].val=tr[x<<1|1].val;
    }

    void maketree(int x,int l,int r)
    {
        tr[x].L=l,tr[x].R=r,tr[x].lazy=tr[x].val=0; int mid=(l+r)>>1;
        if (l<r) maketree(x<<1,l,mid),maketree(x<<1|1,mid+1,r);
    }

    void pushdown(int x)
    {   
        if (tr[x].lazy!=0)
        {
            mark(x<<1,tr[x].lazy);
            mark(x<<1|1,tr[x].lazy);
        }
        tr[x].lazy=0;
    }

    bool modify(int x,int Ql,int Qr,int v)
    {   
        if (Ql>Qr) return true;
        if (Ql<=tr[x].L && tr[x].R<=Qr)
        {   
            if (tr[x].val!=0 && tr[x].val!=v) return false;
            mark(x,v);
            return true; 
        }
        else
        {   
            pushdown(x); bool flag=true;
            int mid=(tr[x].L+tr[x].R)>>1;
            if (Ql<=mid) 
            {
                if (!modify(x<<1,Ql,Qr,v)) return false;
            }
            if (Qr>=mid+1) 
            {
                if (!modify(x<<1|1,Ql,Qr,v)) return false;
            }
            update(x);
            return true;
        }
    }

}SGT;

struct Tree
{
    int n,m,tot,dfn,fst[Maxn],pre[Maxn<<1],to[Maxn<<1],size[Maxn],depth[Maxn],fa[Maxn],top[Maxn],pos[Maxn],son[Maxn]
    ,val[Maxn],ff[Maxn];
    bool vis[Maxn];
    void init() { dfn=tot=0; for (int i=1;i<=n;i++) fst[i]=0,vis[i]=false,val[i]=0; }
    void add(int x,int y)
    {   
        pre[++tot]=fst[x],fst[x]=tot,to[tot]=y;
        pre[++tot]=fst[y],fst[y]=tot,to[tot]=x;
    }

    void dfs1(int x)
    { 
        size[x]=1,vis[x]=true; int maxid=n+1;
        for (int i=fst[x];i;i=pre[i])
        { 
            int y=to[i]; 
            if (!vis[y]) 
            {
                depth[y]=depth[x]+1,fa[y]=x,ff[y]=ff[x],dfs1(y),size[x]+=size[y]; 
                if (size[y]>size[maxid]) maxid=y; 
            }
        }
        son[x]=maxid;
    }

    void dfs2(int x,int ne)
    { 
        top[x]=ne,pos[x]=++dfn,vis[x]=true;
        if (son[x]!=n+1)
        { 
            dfs2(son[x],ne);
            for (int i=fst[x];i;i=pre[i])
            {   
                int y=to[i];
                if (!vis[y] && y!=son[x]) dfs2(y,y); 
            }
        }
    }

    int getlca(int x,int y)
    {
        while (top[x]!=top[y])
        {
            if (depth[top[x]]<depth[top[y]]) swap(x,y);
            x=fa[top[x]];
        }
        if (depth[x]<depth[y]) return x;
        else return y;
    }

    bool check(int x,int y,int v)
    {
        while (top[x]!=top[y])
        {
            if (!SGT.modify(1,pos[top[x]],pos[x],v)) return false;
            x=fa[top[x]];
        }
        if (!SGT.modify(1,pos[y]+1,pos[x],v)) return false;
        return true;
    }

    bool work()
    {
        size[n+1]=-1;
        for (int i=1;i<=n;i++) vis[i]=false;
        for (int i=1;i<=n;i++) if (!vis[i]) ff[i]=i,depth[i]=1,dfs1(i);
        for (int i=1;i<=n;i++) vis[i]=false;
        for (int i=1;i<=n;i++) if (!vis[i]) dfs2(i,i);
        SGT.maketree(1,1,dfn);
        for (int i=1;i<=q;i++) 
        {
            st[i]=belong[st[i]],ed[i]=belong[ed[i]];
            if (ff[st[i]]!=ff[ed[i]]) return false;
            int f=getlca(st[i],ed[i]);
            if (!check(st[i],f,1)) return false;
            if (!check(ed[i],f,2)) return false;
        }
        return true;
    }
}T;

struct Graph
{
    int n,m,tot,dfstot,fst[Maxn],from[Maxn<<1],pre[Maxn<<1],to[Maxn<<1],dfn[Maxn],low[Maxn];
    bool vis[Maxn],flag[Maxn<<1];
    void add(int x,int y)
    {
        pre[++tot]=fst[x],fst[x]=tot,from[tot]=x,to[tot]=y,flag[tot]=false;
        pre[++tot]=fst[y],fst[y]=tot,from[tot]=y,to[tot]=x,flag[tot]=false;
    }

    void init() 
    { 
        n=Get(),m=Get(),q=Get(); tot=1,dfstot=0; int x,y;
        for (int i=1;i<=n;i++) fst[i]=0;
        for (int i=1;i<=m;i++) x=Get(),y=Get(),add(x,y);
        for (int i=1;i<=q;i++) st[i]=Get(),ed[i]=Get();
    }

    void tarjan(int x,int prev)
    {
        vis[x]=true; dfn[x]=low[x]=++dfstot;
        for (int i=fst[x];i;i=pre[i])
        {   
            if (i==(prev^1)) continue;
            int y=to[i];
            if (!vis[y]) tarjan(y,i),low[x]=min(low[x],low[y]);
            else low[x]=min(low[x],dfn[y]);
        }
        if (low[x]==dfn[x]) flag[prev]=flag[prev^1]=true;
    }

    void dfs(int x)
    {
        vis[x]=true; belong[x]=T.n;
        for (int i=fst[x];i;i=pre[i])
        {
            int y=to[i];
            if (!vis[y] && !flag[i]) dfs(y);  
        }
    }   

    void work()
    {   
        for (int i=1;i<=n;i++) vis[i]=false;
        tarjan(1,-1); T.n=0;
        for (int i=1;i<=n;i++) vis[i]=false;
        for (int i=1;i<=n;i++) if (!vis[i]) T.n++,dfs(i);
        T.init();
        for (int i=2;i<=tot;i+=2) if (flag[i]) T.add(belong[from[i]],belong[to[i]]);
    }
}G; 

int main()
{
    G.init();
    G.work();
    if (T.work()) puts("Yes");
    else puts("No");
    return 0;
}

于是乎,我写了B和E题,lxe搞了ACD题(%%%%)结果考场上只过了ABC,还是太弱了,手速太渣

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值