T1 玩具谜题

模拟题,当时没跟上考NOIP2016,之后自己做了一下,T1直接A掉了,Day2T1打暴力拿了30分,开ULL的话有四十分的。如何模拟?0表示向内,1向外,0右数,1做数,用dir表示朝向,lr表示左右数。

注意到: dir^lr 为真是逆时针数,pos=(pos+k)%n。

               dir^lr 为假是顺时针数,pos=(pos+n-k%n)%n.

特别注意:%n后为0是到达了第n个玩具那里。

这样100分就到手了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const int M=100000+10;
 8 char name[M][17]; int n,m,a[M],b,k,pos=1;
 9 int main() {
10     freopen("toya.in","r",stdin);
11     freopen("toya.out","w",stdout);
12     scanf("%d%d",&n,&m);
13     for(int i=1;i<=n;i++)
14         scanf("%d%s",&a[i],name[i]);
15     while(m--) {
16         scanf("%d%d",&b,&k);
17         pos=(a[pos]^b)?(pos+k%n)%n:(pos+n-k%n)%n;
18         if(!pos) pos=n;
19     }
20     printf("%s\n",name[pos]);
21     return 0;
22 }

T2 天天爱跑步

好多人都说这是去年最难的一道题。先抛开正解不谈,这道题的暴力分是很足的。对于本蒟蒻来说,前四个点难度和第一题相当,就是考你语文(读懂题),

看你会不会编程,第五个点n,m<1000,跑暴力DFS统计就可以拿到分了。然后是9~12这四个点,因为所有玩家起点都是1,把1当作树的根节点,1边DFS统计出以该

结点i为根的自树中有多少个终点size[i],回溯的时候判断如果deep[i]=w[i]那么ans[i]+=size[i],这些玩家都回被看到。这样就有45分了。本蒟蒻第一次做只能想到这些。

然而暴力打得好是有80分的。退化为链的三个点貌似是前缀和+二分查找做,终点为1的点也是转化为以1为根的子树,之后再DFS在树上进行一遍统计。这其实已经把

思路往正解上靠了。具体的统计方法在正解里会详细介绍。

现在谈正解。谈什么正解。你还指望考试写正解?45很厉害了好嘛!正解巨难写,而且考场上的话,在努力撸一

撸,应该可以再多拿20~30的暴力分的,那还不上天!靠前一定要摆正心态,暴力出奇迹,千万别想着撸正解,搞

不好百分之80的可能省一无缘,除非您是巨佬~不要跟我说话。

不过本蒟蒻曾经,其实也就是前两星期,%了一发正解,时隔多日,咳咳。还是直接看代码吧,我失忆了。

/* 弱弱地表示即使知道正解也并不好写.
 * 首先要想到一条树上路径可分为 s->lca 的向上路径 和 lca->t 的向下路径.
 * 之后定量表达两种路径上观察员能看到选手的条件.
 * 对于向上路径: dep[s]=dep[u]+w[u];
 * 对于向下路径: dis(s,t)-dep[t]=w[u]-dep[u]. 由于结果可能为负所以要统一加上M便于统计.
 * 这样以后就可以用两个数组A[],B[]直接在DFS时统计.
 * 统计时,对于起点有用的是dep[s],对于终点是dis(s,t)和dep[t].
 * 把两者预处理出来并记录.
 * 特殊的对于lca(s,t)处,是向上路径的终点,也就是说该点的祖先没有可能看到该选手.
 * 所以在lca(s,t)出要减去这一选手对A[],B[]的贡献.
 * 并且,如果lca(s,t)节点的观察员可以看到该选手,那么起终点会各统计一次(向上路径1次,向下路径1次.)
 * 所以要特别判断一下,减去重复的贡献.
 * 一开始没想到也不要紧,因为如果不特判的话,会发现样例1是过不去的.
 * 对于lca(s,t)处,需要记录一名选手的所有信息:s,t,dis(s,t),才能完全消除影响.
 * 写程序时还有很多地方是需要注意的,以及统计的办法.
 */

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
const int M=300000+10;
int n,m,w[M],ans[M],fa[M],dep[M],size[M],son[M],top[M],Os[M],A[M],B[M<<1];
struct LCA { int a,b,c; }; // lca(s,t)处消除影响.
vector<int> G[M],Ot[M]; vector<LCA> Oc[M];
void DFS1(int u,int f,int d) {
    fa[u]=f,dep[u]=d,size[u]=1;
    int s=G[u].size();
    for(int i=0;i<s;i++) {
        int v=G[u][i];
        if(!fa[v]) {
            DFS1(v,u,d+1);
            if(size[v]>size[son[u]]||!son[u]) son[u]=v;
            size[u]+=size[v];
        }
    }
}
void DFS2(int u,int TOP) {
    top[u]=TOP;
    if(!son[u]) return ;
    DFS2(son[u],TOP);
    int s=G[u].size();
    for(int i=0;i<s;i++) {
        int v=G[u][i];
        if(v!=fa[u]&&v!=son[u]) DFS2(v,v);
    }
}
int QLCA(int a,int b) {
    while(top[a]!=top[b]) 
        dep[top[a]]>dep[top[b]]?a=fa[top[a]]:b=fa[top[b]];
    return dep[a]<dep[b]?a:b;
}
void DFS3(int u) {
    int x=w[u]+dep[u],y=w[u]-dep[u]+M;
    int nA=A[x],nB=B[y];
    A[dep[u]]+=Os[u];
    int s=Ot[u].size();
    for(int i=0;i<s;i++)
        B[Ot[u][i]-dep[u]+M]++;
    s=G[u].size();
    for(int i=0;i<s;i++) {
        int v=G[u][i];
        if(v!=fa[u]) DFS3(v);
    }
    ans[u]+=A[x]-nA+B[y]-nB;
    s=Oc[u].size();
    for(int i=0;i<s;i++) {
        LCA p=Oc[u][i];
        A[dep[p.a]]--; // lca是一对一对的,所以不能写成 A[dep[p.a]]-Os[p.a],要一个一个减.
        B[p.c-dep[p.b]+M]--;
        if(x==dep[p.a]) ans[u]--; // y==p.c-dep[p.b]+M
    }
}
int main() {
    freopen("runninga.in","r",stdin);
    freopen("runninga.out","w",stdout);
    
    int __size__=128<<20;
    char *__p__=(char*)malloc(__size__)+__size__;
    __asm__("movl %0,%%esp\n"::"r"(__p__));
    
    scanf("%d%d",&n,&m); int a,b,c;
    for(int i=1;i<n;i++) {
        scanf("%d%d",&a,&b);
        G[a].push_back(b);
        G[b].push_back(a);
    }
    DFS1(1,-1,0),DFS2(1,1);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=m;i++) {
        scanf("%d%d",&a,&b);
        Os[a]++;
        c=QLCA(a,b);
        Ot[b].push_back(dep[a]+dep[b]-(dep[c]<<1)); // 预处理.
        Oc[c].push_back((LCA){a,b,dep[a]+dep[b]-(dep[c]<<1)}); // 预处理.
    }
    DFS3(1);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

T3 换教室

嗯~你觉得我要说什么。COGS坑爹,没有把测试点情况的说明表挂上去,害得我直接去看题解了。看完了,然后呢?都说了考前要摆正心态了。正解,不存在

的。一个DP而已。代码注释写得好清楚了,虽然我现在已无心再写,但那是因为我端正了心态。orz。

/*  gai概概概概率DP,shu数数数数学期望.
 ** 首先再懵逼对于300间教室的数据量也要淡定的floyed求一下最短路.
 *  然后,上大佬们的DP.
 *  状态: f[i][j][0],f[i][j][1] 分别表示到第i个时间段还有j此申请机会并且这个时间段不申请与申请的期望最小值.
 *  好NB的状态.更NB的是状态转移!
 *  好想的是淡定的小牛一节课也不申请,直接就算一遍距离就好了.
 *  对应为方程就是: f[i][0][0]=f[i-1][0][0]+w[c[i-1]][c[i]];
 *  然后复杂一些的,首先得清楚对于每个(i,j)需要且只需要分别计算f[i][j][0]与f[i][j][1].
 ** 对于f[i][j][0]:
 *  只可以从f[i-1][j][0],f[i-1][j][1]转移过来.
 *  因为到i仍旧有j次申请并且i没有申请,所以不牵扯到[j-1]的情况.
 *  solutation1: y1=f[i-1][j][0]+w[c[i-1]][c[i]].
 *  solutation2: A=w[c[i-1]][c[i]]*(1-k[i-1]) 申请未成功.
 *               B=w[d[i-1]][c[i]]*k[i-1] 申请成功.
 *               y2=f[i-1][j][1]+A+B.
 *  result: f[i][j][0]=min(y1,y2).
 ** 对于f[i][j][1]:
 *  只可以从f[i-1][j-1][0],f[i-1][j-1][1]转移过来.
 *  solutation1: y1=f[i-1][j-1][0]+w[c[i-1]][c[i]]*(1-k[i])+w[c[i-1]][d[i]]*k[i].
 *  solutation2 :
 *  由于两次都申请(s1,s2)了,所以共有四种情况:
 *  s1,s2均失败:   x1=w[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i]).
 *  s1失败,时成功:  x2=w[c[i-1]][d[i]]*(1-k[i-1])*k[i].
 *  s1成功,s2失败: x3=w[d[i-1]][c[i]]*k[i-1]*(1-k[i]).
 *  s1,s2均成功:   x4=w[d[i-1]][d[i]]*k[i-1]*k[i].
 *                y2=f[i-1][j-1][1]+x1+x2+x3+x4.
 *  result: f[i][j][1]=min(y1,y2).
 *  这样就完成了!
 *  最后还有一个小小的疑问:
 *  对于一个状态,只把当前这条道路的距离值用两种概率算了一下,但此次成功与否是可以影响到之前的啊!
 *  的确是这样的.
 *  写出来的话就是:
 *  f[i-1][j-1][1]*k[i]+f[i-1][j-1][1]*(1-k[i])=f[i-1][j-1][1].
 *  就是说算不算都一样.
 */

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int M=2000+10,N=300+10,INF=988773321;
int n,m,v,e,c[M],d[M],w[N][N];
double k[M],f[M][M][2];
int main() {
    freopen("classrooma.in","r",stdin);
    freopen("classrooma.out","w",stdout);
    
    scanf("%d%d%d%d",&n,&m,&v,&e); int x,y,z;
    
    for(int i=1;i<=v;i++)
        for(int j=1;j<=v;j++) 
            if(i!=j) w[i][j]=INF;
        
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    for(int i=1;i<=n;i++) scanf("%d",&d[i]);
    for(int i=1;i<=n;i++) scanf("%lf",&k[i]);
    for(int i=1;i<=e;i++) {
        scanf("%d%d%d",&x,&y,&z);
        w[x][y]=w[y][x]=min(w[x][y],z);
    }
    
    for(int K=1;K<=v;K++)
        for(int i=1;i<=v;i++)
            for(int j=1;j<=v;j++)
                w[i][j]=min(w[i][j],w[i][K]+w[K][j]);
    
    for(int i=0;i<=n;i++)
        for(int j=0;j<=m;j++) 
            f[i][j][0]=f[i][j][1]=1e9;
    
    f[1][0][0]=f[1][1][0]=f[1][1][1]=0;
    
    for(int i=2;i<=n;i++) {
        f[i][0][0]=f[i-1][0][0]+w[c[i-1]][c[i]];  // 永远不申请.
        for(int j=1;j<=i&&j<=m;j++) {
            f[i][j][0]=min(f[i-1][j][0]+w[c[i-1]][c[i]],
            f[i-1][j][1]+w[c[i-1]][c[i]]*(1-k[i-1])+w[d[i-1]][c[i]]*k[i-1]);
            f[i][j][1]=f[i-1][j-1][0]+w[c[i-1]][c[i]]*(1-k[i])+w[c[i-1]][d[i]]*k[i];
            double x1=w[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i]);
            double x2=w[c[i-1]][d[i]]*(1-k[i-1])*k[i];
            double x3=w[d[i-1]][c[i]]*k[i-1]*(1-k[i]);
            double x4=w[d[i-1]][d[i]]*k[i-1]*k[i];
            f[i][j][1]=min(f[i][j][1],f[i-1][j-1][1]+x1+x2+x3+x4);
        }
    }
    
    double ans=f[n][0][0];
    for(int i=1;i<=m;i++) 
        ans=min(ans,min(f[n][i][0],f[n][i][1]));
    printf("%.2lf\n",ans);
    return 0;
}

 蒟蒻蒟蒻,大佬们全当娱乐。我感觉怪怪的。