20181102

今天少了100分嘤嘤嘤
orzszorzszorzszorzszorzszorzszorzszorzszorzszorzszorzszorzszorzsz
(切入正题)
T1 2910: 独木桥(bridge)

题目描述

Alice和Bob是好朋友,这天他们带了n个孩子一起走独木桥。
独木桥宽度很窄,不允许两个或两个以上的人并肩行走,所有人必须要前后一个接一个地通行。
Bob给所有的孩子蒙上了眼,并将他们放在桥中不同的位置上,孩子们初始的朝向不一定相同。Bob吹响哨声后这些孩子们会按照初始的朝向开始移动,当两个孩子移动到同一点时由于桥太窄他们无法穿过彼此,因此他们会同时转身改变朝向,并接着朝新方向移动。
为了安全起见,在某个时刻Alice会询问Bob某个孩子现在所处的位置。
更具体的,我们可以将问题抽象如下:
· 将独木桥看作一个长度无限长的实数轴,将每个孩子看作数轴上的一个实数点。数轴从左到右坐标不断增大。
· 孩子的位置用相对于数轴原点的点的坐标来表示。初始时n个点在n个互不相同的整点上。
· 每个点有一个初始朝向(从左向右或从右向左)。任何时刻所有的点都会以每秒1单位长度的速度匀速向所朝的方向移动。当某个时刻两个点同时移动到了同一个位置上,它们会立即改变自己的朝向(从左向右变成从右向左,反之亦然),然后继续移动。
·有q次询问,每次询问给定ki与ti,询问在ti秒后,孩子ki目前的位置。
Bob无法同时关注这么多的孩子,请你帮帮他。
1≤n,q≤2e5, 0≤ki<n, 0≤pi,ti≤1e9,di∈{0,1}

题解

显然每个孩子的顺序是不会变的
可以分别对朝向向左和朝向向右的初始位置进行排序
对于每个询问,我们可以二分答案,然后在两个数组中求出相对这个答案位置更小的点的个数,并且和原来的rank比较
具体见代码吧

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+5;
int n,q,rk[N],s1,s2;LL d1[N],d2[N];
struct O{int d,id;LL p;}a[N];
bool cmp(O A,O B){return A.p<B.p;}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%lld",&a[i].p);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i].d),a[i].id=i;
    sort(a+1,a+n+1,cmp);
    for (int i=1;i<=n;i++){
        rk[a[i].id]=i;
        if (a[i].d) d1[++s1]=a[i].p;
        else d2[++s2]=a[i].p;
    }
    for (scanf("%d",&q);q--;){
        int x;LL y;scanf("%d%lld",&x,&y);
        LL l=a[1].p-y,r=a[n].p+y;
        while(l<r){
            LL mid=l+r>>1;int k1,k2;
            k1=upper_bound(d1+1,d1+s1+1,mid-y)-d1;k1--;
            k2=upper_bound(d2+1,d2+s2+1,mid+y)-d2;k2--;
            if (k1+k2>=rk[x+1]) r=mid;
            else l=mid+1;
        }
        printf("%lld\n",l);
    }
    return 0;
}

T2 2092: 交通 (traffic)

题目描述

格丁尼亚的中心位于Kacza河中的一座岛屿。每天清晨,成千上万辆汽车通过岛屿从西岸的住宅区 (由桥连接岛的西部)到东岸的工业区(由桥连接岛的东部)。
该岛类似于矩形,它的边平行于主方向。故可将它看作是笛卡尔坐标系中的一个A*B的矩形,它的对角分别为(0, 0)和(A, B)。 岛上有n个交通节点,编号为1…n(junction, 此处可理解为广义的路口),第i个节点坐标为(xi, yi)。 如果一个节点的坐标为(0, y),它就位于岛的西岸。类似的,坐标为(A, y)的节点位于岛的东岸。 各个节点由街道连接,每条街道用线段连接两个节点。街道有单向行驶或双向行驶之分。除端点外任意两条街道都没有公共点。也没有桥梁或者隧道。 你不能对道路网络形状做任何其他假设。沿河岸的街道或节点可能没有入口或者出口街道。
由于交通堵塞日趋严重,市长聘请你测试岛上当前的道路网是否足够。要求你写一个程序确定从岛的西岸的每个节点能够到达东岸的多少个节点。
1≤n≤300000, 0≤m≤900000,1≤A,B≤10^9

题解

因为路没有相交,于是我们可以先从左到右bfs一遍,把最右没有遍历到的点去掉,然后从右到左dfs两遍,第一遍从下到上,更新最小值,第二遍从下到上,更新最大值(显然已经被访问的点并不需要继续访问),最后答案就是max-min+1
具体见代码

#include <bits/stdc++.h>
using namespace std;
const int N=3e5+5,M=1800005;
int c,n,m,a,b,id[N],ch,cg;
struct O{int x,y,d;}p[N],g[N],h[N];
int s[N][2];bool vis[N];queue<int>q;
struct E{
    int t,head[N],V[M],nex[M];
    void add(int u,int v){
        V[++t]=v;nex[t]=head[u];head[u]=t;
    }
}e1,e2;
bool cmp(O A,O B){return A.y>B.y;}
void dfs(int x,int v,bool ty){
    s[x][ty]=v;for (int i=e2.head[x];i;i=e2.nex[i])
        if (!s[e2.V[i]][ty]) dfs(e2.V[i],v,ty);
}
int main(){
    scanf("%d%d%d%d",&n,&m,&a,&b);
    for (int i=1;i<=n;i++)
        scanf("%d%d",&p[i].x,&p[i].y),
        p[i].d=i;
    for (int x,y,z,i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        if (z>1) e1.add(y,x),e2.add(x,y);
        e1.add(x,y);e2.add(y,x);
    }
    for (int i=1;i<=n;i++)
        if (!p[i].x) q.push(i),vis[i]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=e1.head[x];i;i=e1.nex[i])
            if (!vis[e1.V[i]]) vis[e1.V[i]]=1,q.push(e1.V[i]);
    }
    for (int i=1;i<=n;i++){
        if (p[i].x==a && vis[i]) h[++ch]=p[i];
        if (p[i].x==0) g[++cg]=p[i];
    }
    sort(g+1,g+cg+1,cmp);sort(h+1,h+ch+1,cmp);
    for (int i=1;i<=ch;i++) dfs(h[i].d,i,0);
    for (int i=ch;i;i--) dfs(h[i].d,i,1);
    for (int i=1;i<=cg;i++)
        printf("%d\n",s[g[i].d][1]?s[g[i].d][1]-s[g[i].d][0]+1:0);
    return 0;
}

T3 3130: 流浪者(rover)

题目描述

有一位流浪者正在一个n∗m的网格图上流浪。初始时流浪者拥有S点体力值。
流浪者会从(1,1)走向(n,m),并且他只会向下走((x,y)→(x+1,y))或是往右走((x,y)→(x,y+1)),在所有可行的路线中他会随机选择一条。
网络图中还有K个障碍点。若流浪者当前体力值为S,则他经过一个障碍点后体力值会变为s/2(向上取整)现在请你求出,流浪者到达(n,m)时他体力值的期望是多少。
1≤n,m≤1e5,0≤K≤min(n∗m,2000),1≤S≤1e6

题解

Fi,jF_{i,j}表示到i点前恰好经过j个点的方案数
由于log2(s)log_2(s)最大20,所以j只需要枚举到20
考虑容斥,我们把起点终点当做障碍点,然后按照x+y排序,设k点是i点的前一点,则可以列出dp式子
Fi,j=Cxi+yi2xi1(k=1i1Fk,jways(k,i))(l=1j1Fl,j)F_{i,j}=C_{xi+yi-2}^{xi-1}-(\sum_{k=1}^{i-1}F_{k,j}*ways(k,i))-(\sum_{l=1}^{j-1}F_{l,j})(ways(k,i)表示从k到i的方案数)
可以自己手推一下,发现这个很有道理
具体见代码

#include <bits/stdc++.h>
#define I inline
#define E register
using namespace std;
const int P=1e9+7,N=2005,Z=2e5+5;
int n,m,k,s,ex[N],ans,jc[Z],ny[Z],o,f[N][N],w[N][N],sum[N][N];
struct O{int x,y;}p[N];I bool M(E O A,E O B){return A.x+A.y<B.x+B.y;}
I int K(E int x,E int y){
    E int A=1;while(y){
        if (y&1) A=1ll*A*x%P;
        x=1ll*x*x%P;y>>=1;
    }
    return A;
}
I int C(E int x,E int y){
    if (x<y || x<0 || y<0) return 0;
    return 1ll*jc[x]*ny[y]%P*ny[x-y]%P;
}
int main(){
    jc[0]=1;for (E int i=1;i<Z;i++) jc[i]=1ll*i*jc[i-1]%P;
    ny[Z-1]=K(jc[Z-1],P-2);for (E int i=Z-1;i;i--) ny[i-1]=1ll*i*ny[i]%P;
    scanf("%d%d%d%d",&n,&m,&k,&s);
    for (E int i=1;i<=k;i++) scanf("%d%d",&p[i].x,&p[i].y);
    ex[0]=s;p[++k]=(O){n,m};p[0]=(O){1,1};
    for (E int i=1;i<=N-1;i++){
        ex[i]=(ex[i-1]+1)>>1;
        if (ex[i]==1 && !o) o=i;
    }
    sort(p,p+k+1,M);f[0][0]=1;
    for (E int i=0;i<=k;i++) for (E int j=0;j<i;j++) 
        w[i][j]=C(p[i].x-p[j].x+p[i].y-p[j].y,p[i].x-p[j].x);
    for (E int i=1;i<=k;i++) for (E int j=0;j<=o+1;j++){
        if (j) f[i][j]=(C(p[i].x+p[i].y-2,p[i].x-1)-sum[i][j-1]+P)%P;
        else f[i][j]=C(p[i].x+p[i].y-2,p[i].x-1);
        for (E int l=0;l<i;l++)
            f[i][j]=(f[i][j]-1ll*f[l][j]*w[i][l]%P+P)%P;
        if (j) sum[i][j]=(sum[i][j-1]+f[i][j])%P;
        else sum[i][j]=f[i][j];
    }
    for (E int i=1;i<=o;i++)
        ans=(ans+1ll*f[k][i]*K(C(n+m-2,n-1),P-2)%P*ex[i-1]%P)%P;
    ans=(ans+1ll*(C(n+m-2,n-1)-sum[k][o]+P)%P*K(C(n+m-2,n-1),P-2)%P)%P;
    return printf("%d\n",ans),0;
}
posted @ 2018-11-02 18:45  xjqxjq  阅读(288)  评论(0编辑  收藏  举报