20181031

orzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjardenorzjarden
T1 4014: 铃仙的红色之瞳(eyes)

题目描述

为了方便你的预测,铃仙对该符卡进行了改造。
敌方非常强大,可以看作有无限的体力。通过该符卡,铃仙可以释放出子弹,敌方触碰到子弹就会损失一格体力。注意,每次敌方损失体力之后,其位置不会改变。
当敌方和铃仙 x 坐标相同上时,每秒铃仙损失一点体力(敌方的攻击比较特殊,可以对前后都进行攻击)。注意,这里的秒指的是时间间隔,即第 A 秒时候敌方开始与铃仙处于一条直线上,第 B 秒时候敌方离开这条直线,那么铃仙受到的伤害是(B-A)点。特殊地,如果刚开始铃仙就和敌方在一条直线上,则第 0 秒~第 1 秒也受到一点伤害。若最后铃仙和敌方在 x 坐标相同的位置或者敌方处于有子弹的位置上时,它们只会在第 Q-1 秒到第 Q 秒时受到伤害,并不会在第 Q 秒到第 Q+1 秒受到伤害。
地图是一个 n*m 的矩形,分别对应的 x 坐标为[1,n],y 坐标为[1,m],给定敌方的初始位置和铃仙的位置(铃仙不会移动),当铃仙发出的子弹超过边界时子弹消失,当敌方越过边界时敌方自动回到初始位置。
现在有 Q 秒,每秒一次操作,可能是铃仙的操作也可能是敌方的操作,可能存在的操作如下:
Ins x y 铃仙在(x,y)处召唤了一颗子弹(如果原先就有子弹则该操作无效)
Del x y 铃仙收回法力,让(x,y)处的子弹消失(如果原先没有子弹则该操作无效)
MA U 铃仙将所有子弹上移一格
MA D 铃仙将所有子弹下移一格
MA L 铃仙将所有子弹左移一格
MA R 铃仙将所有子弹右移一格
MB U 敌方上移一格
MB D 敌方下移一格
MB L 敌方左移一格
MB R 敌方右移一格
现在要你计算 Q 秒后敌方损失的体力和铃仙损失的体力。
(注:左移指的是横坐标-1,下移指的是纵坐标-1)
n,m<=100,Q<=1000

题解

模拟,走一遍图即可,注意伤害的计算位置(被坑了30 QAQ)
具体见代码

#include <bits/stdc++.h>
using namespace std;
int Case,n,m,ax,ay,bx,by,cx,cy,Q,t1,t2;
char c[4],ch;bool a[105][105],b[105][105];
int main(){
    scanf("%d%d%d%d%d%d%d%d",&Case,&n,&m,&ax,&ay,&bx,&by,&Q);
    cx=bx;cy=by;
    for (int x,y,dx,dy;Q--;){
        if (cx==ax) t2++;if (a[cx][cy]) t1++;scanf("%s",c);
        if (c[0]=='I') scanf("%d%d",&x,&y),a[x][y]=1;
        else if (c[0]=='D') scanf("%d%d",&x,&y),a[x][y]=0;
        else if (c[1]=='A'){
            scanf(" %c",&ch);
            if (ch=='U') dx=0,dy=1;
            if (ch=='D') dx=0,dy=-1;
            if (ch=='L') dx=-1,dy=0;
            if (ch=='R') dx=1,dy=0;
            for (int i=1;i<=n;i++)
                for (int j=1;j<=m;j++)
                    b[i][j]=a[i-dx][j-dy];
            for (int i=1;i<=n;i++)
                for (int j=1;j<=m;j++)
                    a[i][j]=b[i][j],b[i][j]=0;
        }
        else{
            scanf(" %c",&ch);
            if (ch=='U') cy++;if (ch=='D') cy--;
            if (ch=='L') cx--;if (ch=='R') cx++;
            if (cx<1 || cx>n || cy<1 || cy>m) cx=bx,cy=by;
        }
    }
    printf("%d\n%d\n",t1,t2);
    return 0;
}

T2 4015: 永琳的竹林迷径(path)

题目描述

竹林可以看作是一个n 个点的树,每个边有一个边长wi,其中有k 个关键点,永琳需要破坏这些关键点才能走出竹林迷径。
然而永琳打算将这k 个点编号记录下来,然后随机排列,按这个随机的顺序走过k 个点,但是两点之间她只走最短路线。初始时永琳会施展一次魔法,将自己传送到选定的k 个点中随机后的第一个点。
现在永琳想知道,她走过路程的期望是多少,答案对998244353 取模。
n,k<=1e6

题解

考虑每条边的贡献
通过dfs算出在每条边两端的点分别有多少个是属于k的,假设一边有s个,另一边就有k-s个
对于那s个中的某一个,如果说在走的排列中,它的前一个或者后一个是那k-s个中的一个,那说明这条边就多了一次的贡献
所以这条边的贡献就是2(ks)/ksw[i]2*(k-s)/k*s*w[i]
最后相加即可
具体见代码

#include <bits/stdc++.h>
#define _(d) while(d(isdigit(c=getchar())))
#define I inline
#define LL long long
#define E register
using namespace std;
I int R(){
    int x;char c;_(!);x=(c^48);
    _()x=(x<<3)+(x<<1)+(c^48);return x;
}
const int N=1e6+5,P=998244353;
int Case,n,t,head[N],nex[N*2],V[N*2],W[N*2],k,top[N],fa[N],dep[N],sz[N],son[N],g[N];
LL h[N],ans;bool p[N];
I void add(E int u,E int v,E int w){
    V[++t]=v;W[t]=w;nex[t]=head[u];head[u]=t;
}
I void ins(){
    int u=R(),v=R(),w=R();
    add(u,v,w);add(v,u,w);
}
I LL K(E LL x,E LL y){
    LL A=1;while(y){
        if (y&1) A=A*x%P;
        x=x*x%P;y>>=1;
    }
    return A;
}
I int dfs(E int x,E int fa){
    int s=0,tot=0;
    for (int i=head[x];i;i=nex[i]){
        if (V[i]==fa) continue;
        s=dfs(V[i],x);tot+=s;
        ans=(2ll*s*(k-s)%P*W[i]%P+ans)%P;
    }
    return tot+p[x];
}
int main(){
    Case=R();n=R();for (E int i=1;i<n;i++) ins();
    k=R();for (E int i=1;i<=k;i++) p[R()]=1;dfs(1,0);
    return printf("%lld\n",ans*K(k,P-2)%P),0;
}

T3 4016: 辉夜的夜空明珠(moon)

题目描述

整个回廊可以看作一个n 个点m 条边的无向图,每条边走动花费的时间为1。辉夜、永琳、铃仙、因幡帝等k 个人或兔子可以通过传送阵分别进入这个图上的k 个特殊的点,然后去寻找闯入者。但是在寻找闯入者之前,他们要聚集到一个点,以增强战斗力。注意,可以先到的人停下不走等后来的人。

闯入者不知道回廊的规则,因此被困住,对辉夜等k 个人的行动没有影响。而辉夜等k个人必须按照回廊的规则走动。

回廊的规则如下:每个点有一个颜色,一共4 种颜色,红、蓝、黄、绿,分别以R、B、Y、G 表示。走动时必须在第4p+1 步到4p+4 步的时候走四种不同的颜色,当然最后一个不完整的周期内也不能走动相同颜色。注意,起点算第1 步。

现在给定k 个起点,辉夜想知道他们最短多长时间能够汇合,若不能汇合输出-1。
n<=50000 m<=200000 k<=10

题解

因为k很小,所以我们可以从k下手
要求最短汇合时间,所以我们可以对那k个点,每个点都去跑一个最短路,最后的答案就是哪个点的最短路的最大值的最小值
但是这里的最短路是有带限制的,也就是每个周期内的颜色不能相同,但是因为只有4种颜色,所以我们可以设d[i][j]表示到i点,这个周期颜色状态为j的最小值
直接写bfs就可以过,考场上写了dijstra_heap,也可以过
具体见代码

#include <bits/stdc++.h>
using namespace std;
const int N=5e4+2,M=4e5+2;
int head[N],V[M],nex[M],t,Case,n,m,k,p[12],c[N],ans=1e9;
char ch;bool vis[17][N];int d[17][N],cs[N];
void add(int u,int v){
    V[++t]=v;nex[t]=head[u];head[u]=t;
}
struct O{
    int x,d,g;
    friend bool operator < (const O& A,const O& B){
        return A.d>B.d;
    }
};
priority_queue<O>q;
void work(int x){
    for (int j=0;j<16;j++) for (int i=1;i<=n;i++) d[j][i]=1e9,vis[j][i]=0;
    d[(1<<c[p[x]]-1)][p[x]]=0;q.push((O){p[x],0,(1<<c[p[x]]-1)});
    while(!q.empty()){
        O K=q.top();q.pop();
        if (vis[K.g][K.x]) continue;
        vis[K.g][K.x]=1;
        int r=K.g;if (r==15) r=0;
        for (int i=head[K.x];i;i=nex[i]){
            if (r&(1<<c[V[i]]-1)) continue;
            if (d[r^(1<<c[V[i]]-1)][V[i]]>d[K.g][K.x]+1){
                d[r^(1<<c[V[i]]-1)][V[i]]=d[K.g][K.x]+1;
                q.push((O){V[i],d[r^(1<<c[V[i]]-1)][V[i]],r^(1<<c[V[i]]-1)});
            }
        }
    }
    for (int i=1;i<=n;i++){
        int L=1e9;
        for (int j=0;j<16;j++) L=min(L,d[j][i]);
        cs[i]=max(cs[i],L);
    }
}
int main(){
    freopen("moon.in","r",stdin);freopen("moon.out","w",stdout);
    scanf("%d%d%d%d",&Case,&n,&m,&k);
    for (int i=1;i<=k;i++) scanf("%d",&p[i]);
    for (int i=1;i<=n;i++){
        scanf(" %c",&ch);
        if (ch=='R') c[i]=1;
        if (ch=='B') c[i]=2;
        if (ch=='Y') c[i]=3;
        if (ch=='G') c[i]=4;
    }
    for (int u,v,i=1;i<=m;i++)
        scanf("%d%d",&u,&v),add(u,v),add(v,u);
    for (int i=1;i<=k;i++) work(i);
    for (int i=1;i<=n;i++) ans=min(cs[i],ans);
    if (ans>=1e9) puts("-1");
    else printf("%d\n",ans);
    return 0;
}

总的来说,jarden太强了(rk1的巨佬)

posted @ 2018-11-01 13:35  xjqxjq  阅读(133)  评论(0编辑  收藏  举报