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; }
蒟蒻蒟蒻,大佬们全当娱乐。我感觉怪怪的。
浙公网安备 33010602011771号