Codefoces310 div1
2016-01-04 17:23
302 查看
昨晚陪lxe大神组队随意找了一场div1来打
第一打div1非常激动,感觉题目一定会坑爹的不行
结果……
A题
n组套娃,保证所有套娃合起来是1-m,求最少的合并操作数
傻逼题。把包含1的那坨找出来,除了1,2,3…的以外,其它打开暴力合并
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]。然后我们考虑每座桥用来连那一座,显然,如果左区间满足,肯定选右区间最小的那一个。从左到右扫一遍,拿个堆维护就好了。
C题
给你一个腰长为n的等腰直角三角形,还有m个操作,每次要吗从底边往左边的腰上砍一刀,要吗往右边的腰上砍一刀,但是如果砍到之前的刀痕就停止。每次砍不会真正的砍下来,求每次砍下的面积。(n<=10^9,m<=100000)
傻逼线段树。发现左边的只有右边的有影响,且刚好是那个点上的最大值会有影响。所以左右各建一颗线段树,然后每次查询,把查询的出的区间再修改就好了。
D题:
数轴上有n个点,m个询问,每次询问从一个点开始,有一条长度为l的绳子,从这个点开始,绳子开始打转,打转到最远的那个点,就定住,剩下的长度接着转,求最后停在那(n,m<=200000)
傻逼模拟。lxe说可以证明每次暴力模拟,加点取模优化,次数不会很多(似乎是log2(n)级别的?)反正我不知道。
E题:
给你一个n个点m条边的图,再给你若干K个起始点,终止点,你要规定每条边的方向,使得从每个起始点开始,可以走到对应的终止点。求是否存在这样的方案。(n,m,K<=200000)
傻逼tarjan缩点+树剖+线段树。显然对于一个双联通分量中的点,不管怎么定方向,都可以互相到达。然后用tarjan缩一发点,变成一棵树。那么显然,对于一条起始点为u,终止点为v的路径,可以使得,u->lca(u,v)的边变得朝向根的方向,lca(u,v)->v的边变得朝向根方向的相反方向,然后树剖+线段树维护一下,判断一下是否矛盾就好了。
反正这题我剩1h15min开始敲,各种复制模板,然后考后5min调完……236行……(写得我想艹)而且最坑爹的是我发现好像有更快的不用写树剖的方法。。。。。他妈的就我写了树剖,其它都是什么并查集之类的,反正我这种蒟蒻是不会。。。。。
于是乎,我写了B和E题,lxe搞了ACD题(%%%%)结果考场上只过了ABC,还是太弱了,手速太渣
第一打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 ); } 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,还是太弱了,手速太渣
相关文章推荐
- SparkStreaming实战
- iOS 9 真机调试
- 后现代的系统编程语言——C++
- C语言链表的概念
- 内核模式和用户模式
- Laravel 学习总结二:基础内容(Route)
- Eclipse插件之编码主题插件---Eclipse Color Theme
- Nginx的平滑重启和平滑升级
- android集成websocket下载方式
- 摇一摇结合iBEacon
- mysql备份和还原(2)
- JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue
- xml转为javaBean
- JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue
- 关于webdriver中弹出框的定位
- mybatis--mysql 批量插入批量更新在一条sql语句中完成
- 使用PHP实现微信摇一摇周边红包
- ArrayBlockingQueue和LinkedBlockingQueue的区别
- 一些sqoop脚本任务
- Android的学习笔记1