【题解】NOIP2016提高组 复赛
【题解】NOIP2016提高组 复赛
传送门:
- 玩具谜题 \(\text{[P1563]}\)
- 天天爱跑步 \(\text{[P1600]}\)
- 换教室 \(\text{[P1850]}\)
- 组合数问题 \(\text{[P2822]}\)
- 蚯蚓 \(\text{P[2827]}\)
- 愤怒的小鸟 \(\text{P[2831]}\)
【Day1】
【T1】
【题目描述】
有 \(n\) \((n \leqslant 10^5)\) 个小人围成一圈(逆时针给出),已知它们的姓名 \(name[i]\) 和面朝的方向 \(a[i]\)(内或外)。
现从位置 \(1\) 开始,给出 \(m\) 条指令,每条指令将给出移动方向 \(x\)(向左或向右)和移动距离 \(y\),输出每次执行指令后所在位置的小人姓名。
【分析】
随便膜你一下就好了。
用一个变量 \(p\) 维护当前所在位置,为方便环上取膜处理,把 \([1,n]\) 映射为 \([0,n-1]\) 。
\(a[p]=0,\) \(x=0\):朝内,向左。小人编号 \(-y\) 。
\(a[p]=0,\) \(x=1\):朝内,向右。小人编号 \(+y\) 。
\(a[p]=1,\) \(x=0\):朝外,向左。小人编号 \(+y\) 。
\(a[p]=1,\) \(x=1\):朝外,向左。小人编号 \(-y\) 。
可以发现 \(a[p]\) 与 \(x\) 的异或值为 \(1\) 时编号为增加,为 \(0\) 时减少。
没什么细节,也没什么坑点,这才是真正的送分题。
【Code】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define Re register int
using namespace std;
const int N=1e5+3;
int n,m,x,y,a[N];char name[N][13];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
int main(){
// freopen("toy.in","r",stdin);
// freopen("toy.out","w",stdout);
in(n),in(m);
for(Re i=0;i<n;++i)in(a[i]),scanf("%s",name[i]);
Re p=0;
while(m--){
in(x),in(y);
x^=a[p];
if(x)p=(p+y)%n;
else p=(p-y+n)%n;
}
printf("%s",name[p]);
fclose(stdin);
fclose(stdout);
return 0;
}
【T2】
【题目描述】
给出一棵 \(n\) \((n \leqslant 3*10^5)\) 个节点的树,每个节点上的观察员会在 \(w_{i}\) \((0 \leqslant w_{i} \leqslant n)\) 时进行观测,如果此时恰好有玩家在此节点上,那么观察员可以观测到该玩家。
现给出 \(m\) \((m \leqslant 3*10^5)\) 个玩家的运动路线起点 \(st_{j}\) \((1 \leqslant st_{j} \leqslant n)\)、终点 \(ed_{j}\) \((1 \leqslant ed_{j} \leqslant n)\),在第 \(0\) 秒时,所有人都会同时出发直至到达终点。
求每个观察员可以观测到多少个玩家。
【分析】
一道码农题。
对于每个节点 \(x\),考虑在它的子树中能对它产生贡献的点有哪些:
\((1).\) 玩家起点在 \(x\) 的子树中。需满足 \(deep[st_{j}]=deep[x]+w[x]\) 。
\((2).\) 玩家终点在 \(x\) 的子树中。需满足 \(dis(st_{j},ed_{j})=w[x]+(deep[ed_{j}]-deep[x])\),易知 \(dis(st_{j},ed_{j})=\) \(deep[st_{j}]+deep[ed_{j}]-2*deep[lca(st_{j},ed_{j})]\),化简得:\(deep[st_{j}]-2*deep[lca(st_{j},ed_{j})]=w[x]-deep[x]\) 。
于是问题被转换成了:在 \(x\) 的子树中找到满足上述等式的 \(j\) 的个数。
为方便描述,将上述两个等式视为 \(s_{1}=deep[x]+w[x]\) 和 \(s_{2}=w[x]-deep[x]\)。
一种方法是开桶然后直接差分,但智商不够,就只有数据结构来凑了。
对每个节点开一棵动态开点权值线段树,对于每条路径 \(dis(st_{j},ed_{j})\),把路径 \(dis(st_{j},lca(st_{j},ed_{j}))\) 上的所有线段树的 \(s_{1}\) 都加 \(1\),\(dis(ed_{j},lca(st_{j},ed_{j}))\) 上的所有线段树的 \(s_{2}\) 都减 \(1\),这个操作可以通过树上差分+线段树合并实现。
总结:\(lca\) \(+\) 树上差分 \(+\) 权值线段树 \(+\) 动态开点 \(+\) 线段树合并。豆粽强者,恐怖如斯。。
其实用树剖应该会更简单的啦。
【Code】
#include<algorithm>
#include<cstdio>
#define Re register int
using namespace std;
const int N=3e5+3,logN=19;
int x,y,n,m,T,o,lca,w[N],pt[N],ans[N],head[N],deep[N];
struct QWQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
struct Segmemt_Tree{
#define pl tr[p].lp
#define pr tr[p].rp
#define mid (L+R>>1)
struct QAQ{int g,lp,rp;}tr[N*36];int cnt;//大约要开4nlog(2n)个节点
inline void change(Re &p,Re L,Re R,Re x,Re v){
if(!p)p=++cnt;
if(L==R){tr[p].g+=v;return;}
if(x<=mid)change(pl,L,mid,x,v);
else change(pr,mid+1,R,x,v);
}
inline int merge(Re p,Re q){
if(!p)return q;if(!q)return p;
tr[p].g+=tr[q].g;
pl=merge(pl,tr[q].lp);
pr=merge(pr,tr[q].rp);
return p;
}
inline int ask(Re p,Re L,Re R,Re x){
if(!p)return 0;
if(L==R)return tr[p].g;
if(x<=mid)return ask(pl,L,mid,x);
else return ask(pr,mid+1,R,x);
}
}T1;
struct LCA{//倍增lca
int ant[N][23];
inline void dfs(Re x,Re fa){
deep[x]=deep[ant[x][0]=fa]+1;
for(Re i=1;(1<<i)<=deep[x];++i)ant[x][i]=ant[ant[x][i-1]][i-1];
for(Re i=head[x];i;i=a[i].next)if(a[i].to!=fa)dfs(a[i].to,x);
}
inline int lca(Re x,Re y){
if(deep[x]<deep[y])swap(x,y);
for(Re i=logN;i>=0;--i)if(deep[ant[x][i]]>=deep[y])x=ant[x][i];
if(x==y)return x;
for(Re i=logN;i>=0;--i)if(ant[x][i]!=ant[y][i])x=ant[x][i],y=ant[y][i];
return ant[x][0];
}
}T2;
inline void dfs(Re x,Re fa){
for(Re i=head[x],to;i;i=a[i].next)
if((to=a[i].to)!=fa)
dfs(to,x),pt[x]=T1.merge(pt[x],pt[to]);//合并子树信息
ans[x]+=T1.ask(pt[x],1,n<<1,n+deep[x]-w[x]);
if(w[x]&&deep[x]+w[x]<=n)ans[x]+=T1.ask(pt[x],1,n<<1,n+deep[x]+w[x]);
//当w[x]为0时会算两遍所以要特判,还有不能越界
}
int main(){
// freopen("running.in","r",stdin);
// freopen("running.out","w",stdout);
in(n),in(T),m=n-1;
while(m--)in(x),in(y),add(x,y),add(y,x);
for(Re i=1;i<=n;++i)in(w[i]);
T2.dfs(1,0);
while(T--){
in(x),in(y),lca=T2.lca(x,y);
T1.change(pt[x],1,n<<1,n+deep[x],1);//可能会有负数,把所有下标都加n
T1.change(pt[y],1,n<<1,n+(deep[lca]<<1)-deep[x],1);
T1.change(pt[lca],1,n<<1,n+deep[x],-1);
T1.change(pt[T2.ant[lca][0]],1,n<<1,n+(deep[lca]<<1)-deep[x],-1);
}
dfs(1,0);
for(Re i=1;i<=n;++i)printf("%d ",ans[i]);
fclose(stdin);
fclose(stdout);
return 0;
}
【T3】
【题目描述】
有 \(ng\) \((ng \leqslant 2000)\) 个时间点,第 \(i\) 个时间点有两个教室可使用(默认使用 \(A_{0}[i]\)),可以提交最多 \(mg\) \((mg \leqslant 2000)\) 个申请,每次申请有 \(K_{i}\) 的概率通过,如果通过,那么第 \(i\) 个时间点的使用教室将会由 \(A_{0}[i]\) 换成 \(A_{1}[i]\) 。
一共 \(n\) \((n \leqslant 300)\) 个教室 \(m\) \((m \leqslant 90000)\) 条边构成一张无向图,每上完第 \(i\) 个时间点的课后,会选择一条最短路径走向第 \(i+1\) 个时间点的教室。
求:在哪几个时间点上申请交换教室可以使得所走的总路径期望值最小,只需要输出这个最小值即可。
【分析】
本以为 \(T2\) 已经够毒瘤了,没想到 \(T3\) 更 \(\text{bt}\) 。
第一次做期望题,一脸懵逼的我就只能在子任务中寻找骗分点。
特殊性质1:一颗树?然并卵。n<=300的完全图你能卡我?你要是能卡我,我就,我就...
特殊性质2:凡是选了出来的一定会交换
观察性质 \(2\),发现一切与概率有关的东西都可以忽略,直接上 \(dp\),再加上 \(n=1\) 的特判(输出 \(0.00\)),\(28\) 分就到手了,做法如下:
由于图是固定不变的,可以用 \(Floyed\) 预处理出每对点的最短路,也可以用 \(dijkstra\)(理论时间复杂度优一些)。
\(dp[k][i][0]\) 表示已经在前 \(i\) 个时间点中选出了 \(k\) 个,且第 \(i\) 个不选的总路径最小值,
\(dp[k][i][1]\) 表示已经在前 \(i\) 个时间点中选出了 \(k\) 个,且第 \(i\) 个被选的总路径最小值。
那么有:
\]
\]
现在考虑加入概率,状态表示不变,递推形式也基本相同,但转移的时候就需要变化一下。
假设第 \(i\) 个时间点没有选,那么一定是在 \(A_{0}[i]\),即有 \(1\) 的概率在 \(A_{0}[i]\),有 \(0\) 的概率在 \(A_{1}[i]\)。如果选了,那么将有 \(K_{i}\) 的概率在 \(A_{1}[i]\),\(1\!-\!K[i]\) 的概率在 \(A_{0}[i]\),
因此在计算 \(dis(A_{x_{i-1}}[i\!-\!1],A_{x_{i}}[i])\) 的时候就应乘上 \(x_{i-1}\) 的概率 \(P_{x_{i-1}}(i\!-\!1)\) 再乘以 \(x_{i}\) 的概率 \(P_{x_{i}}(i)\),即:
\(dp[k][i][0/1]=min\{dp[k][i][0/1]+dis(A_{0/1}[i\!-\!1],A_{0/1}[i])*P_{0/1}(i\!-\!1)*P_{0/1}(i)\}\)
对于 \(dp[k][i][0]\),\(P_{0}(i)=1,\) \(P_{1}(i)=0\),
对于 \(dp[k][i][1]\),\(P_{0}(i)=1\!-\!K[i],\) \(P_{1}(i)=K[i]\) 。
方程见代码。
时间复杂度为:\(O(n^2)\) 。
【Code】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#define LD double
#define Re register int
using namespace std;
const int N=303,M=90003,G=2003,inf=1e9,eps=1e-8;
int n,m,x,y,z,o,ng,mg,flagT2=1,A[G][2],head[N];LD ans,K[G],dp[G][G][2];
struct QAQ{int w,to,next;}a[M<<1];
inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Dijkstra{
struct QWQ{int x,d;inline bool operator<(QWQ O)const{return d>O.d;};};
int pan[N],dis[N];priority_queue<QWQ>Q;
inline void dijkstra(Re st){
for(Re i=0;i<=n;++i)dis[i]=inf;
Q.push((QWQ){st,dis[st]=0});
while(!Q.empty()){
Re x=Q.top().x;Q.pop();
if(pan[x])continue;
pan[x]=1;
for(Re i=head[x],to;i;i=a[i].next)
if(dis[to=a[i].to]>dis[x]+a[i].w)
Q.push((QWQ){to,dis[to]=dis[x]+a[i].w});
}
}
}T1[N];
inline int dis(Re x,Re y){return T1[x].dis[y];}
int main(){
// freopen("classroom.in","r",stdin);
// freopen("classroom.out","w",stdout);
in(ng),in(mg),in(n),in(m);
for(Re i=1;i<=ng;++i)in(A[i][0]);
for(Re i=1;i<=ng;++i)in(A[i][1]);
for(Re i=1;i<=ng;++i){
scanf("%lf",&K[i]);
// flagT2&=(K[i]==1.0);
}
while(m--)in(x),in(y),in(z),add(x,y,z),add(y,x,z);
if(ng==1){
printf("0.00");
fclose(stdin);
fclose(stdout);
return 0;
}
for(Re i=1;i<=n;++i)T1[i].dijkstra(i);
// if(flagT2){//特殊性质: K[i]全部等于1
// for(Re k=0;k<=mg;++k)
// for(Re i=0;i<=ng;++i)
// dp[k][i][0]=dp[k][i][1]=inf;
// dp[0][1][0]=0;
// for(Re i=2;i<=ng;++i)dp[0][i][0]=dp[0][i-1][0]+dis(A[i-1][0],A[i][0]);
// for(Re k=1;k<=mg;++k)
// for(Re i=k;i<=ng;++i){
// dp[k][i][0]=min(dp[k][i-1][0]+dis(A[i-1][0],A[i][0]),dp[k][i-1][1]+dis(A[i-1][1],A[i][0]));
// dp[k][i][1]=min(dp[k-1][i-1][0]+dis(A[i-1][0],A[i][1]),dp[k-1][i-1][1]+dis(A[i-1][1],A[i][1]));
// }
// ans=inf;
// for(Re k=0;k<=mg;++k)ans=min(ans,min(dp[k][ng][0],dp[k][ng][1]));
// printf("%.2lf\n",ans);
// }
// else{
for(Re k=0;k<=mg;++k)
for(Re i=1;i<=ng;++i)//dp[0][][]手动初始化,不用设inf
dp[k][i][0]=dp[k][i][1]=1000000000.0;
dp[1][1][1]=dp[0][1][0]=0;//i=1的情况手动初始化
for(Re i=2;i<=ng;++i)dp[0][i][0]=dp[0][i-1][0]+(LD)dis(A[i-1][0],A[i][0]);
for(Re i=2;i<=ng;++i)//从i=2开始跑
for(Re k=1;k<=mg&&k<=i;++k){
dp[k][i][0]=min(
dp[k][i-1][0]
+(LD)dis(A[i-1][0],A[i][0]),
dp[k][i-1][1]
+(LD)dis(A[i-1][1],A[i][0])*K[i-1]
+(LD)dis(A[i-1][0],A[i][0])*(1.0-K[i-1])
);
dp[k][i][1]=min(
dp[k-1][i-1][0]
+(LD)dis(A[i-1][0],A[i][1])*K[i]
+(LD)dis(A[i-1][0],A[i][0])*(1.0-K[i]),
dp[k-1][i-1][1]
+(LD)dis(A[i-1][1],A[i][1])*K[i-1]*K[i]
+(LD)dis(A[i-1][0],A[i][1])*(1.0-K[i-1])*K[i]
+(LD)dis(A[i-1][1],A[i][0])*K[i-1]*(1.0-K[i])
+(LD)dis(A[i-1][0],A[i][0])*(1.0-K[i-1])*(1.0-K[i])
);
}
ans=dp[0][ng][0];
for(Re k=1;k<=mg;++k)ans=min(ans,min(dp[k][ng][0],dp[k][ng][1]));
printf("%.2lf\n",ans);
// }
fclose(stdin);
fclose(stdout);
return 0;
}
【Day2】
【T1】
【题目描述】
给出 \(T\) \((T \leqslant 10^4)\) 组数据和一个整数 \(K\) \((K \leqslant 21)\),每组数据给出两个整数 \(n,m\) \((n,m \leqslant 2000)\),求满足 \(C^{j}_{i}\) 能被 \(K\) 整除的 \(i,j\) \((i \in [1,n],j\in[1,min(i,m)])\) 对数。
【分析】
组合数递推公式为 \(C^{m}_{n}=C^{m}_{n-1}+C^{m-1}_{n-1}\),先 \(n^2\) 预处理一下,又由于 \(K\) 是固定的,所以在递推的时候顺带取个膜,后面就直接判断 \(C^{j}_{i}\) 是否为 \(0\)。
查询时 \(n^2\) 暴力扫描可以得 \(90\) 分(送分题就是不一样,暴力给 \(90\)),考虑用矩阵前缀和优化:
\(S[j][i]=S[j\!-\!1][i]+S[j][i\!-\!1]\!-\!S[j\!-\!1][i\!-\!1]+(C[j][i]==0)\)
但是当 \(j==i\) 时,使用到的 \(S[j][i-1]\) 在上一层循环中没有被更新到,所以在递推求 \(S\) 的时候枚举循环是允许 \(j>i\) 的(也可以直接特判 \(S[j][i]=S[j\!-\!1][i]+(C[j][i]==0)\))。
时间复杂度为:\(O(n^2+T)\) 。
【Code】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define Re register int
using namespace std;
const int N=2003;
int x,y,T,K,C[N][N],S[N][N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
int main(){
// freopen("problem.in","r",stdin);
// freopen("problem.out","w",stdout);
in(T),in(K);
for(Re i=1;i<=2000;++i)C[0][i]=C[i][i]=1;
for(Re i=2;i<=2000;++i)
for(Re j=1;j<=i;++j)
C[j][i]=(C[j][i-1]+C[j-1][i-1])%K;
for(Re i=1;i<=2000;++i)
for(Re j=1;j<=2000;++j)
S[j][i]=S[j-1][i]+S[j][i-1]-S[j-1][i-1]+(j<=i&&C[j][i]==0);
while(T--)in(x),in(y),printf("%d\n",S[y][x]);
fclose(stdin);
fclose(stdout);
return 0;
}
【T2】
【题目描述】
给出 \(n\) \((n \leqslant 10^5)\) 只蚯蚓的长度 \(x_{i}\),每秒选出其中最长的一只将其砍为 \(\lfloor px_{i} \rfloor\) \((0<p<1)\) 和 \(x_{i}-\lfloor px_{i}\rfloor\) 两只,随后所有蚯蚓都会伸长 \(q\) \((0 \leqslant q \leqslant 200)\)。输出第 \(t,2t,3t..\lfloor\frac{m}{t}\rfloor\) \((1 \leqslant t \leqslant 71)\) 秒被切断的蚯蚓(在被切之前)长度以及 \(m\) \((0 \leqslant m \leqslant 7*10^6)\) 秒之后第 \(t,2t,3t...\lfloor\frac{n+m}{t}\rfloor\) 长的蚯蚓长度。
【分析】
由于每次砍出来的两半长度一定小于等于砍之前,所以每次选出来要砍的蚯蚓长度 \(x\) 一定是单调不上升的,那么 \(\lfloor px\rfloor\) 和 \(x-\lfloor px\rfloor\) 也应该是单调不上升的,直接开三个队列模拟一下就好了。
还要解决每秒长度的增加量 \(q\),老老实实的加是肯定不行的,庞大的数据量用 \(sort\) 都可能 \(TLE\),更别说什么数据结构之类的东西了。但可以记录一个 \(dlt\),表示当前已经欠了 \(dlt\) 没有加,而元素出队时只要加上这个值就可以表示真实长度。
【Code】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#define LD double
#define Re register int
using namespace std;
const int N=1e5+3,M=7e6+3;
int x,n,m,q,u,v,t,tmp,a[N],H[3]={1,1,1},T[3],Q[3][N+M];LD p;
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline int Top(){//找到三个队列中最长的一个出队
Re p,ans=-2e9;
for(Re i=0;i<3;++i)
if(H[i]<=T[i]&&Q[i][H[i]]>ans)ans=Q[p=i][H[i]];
++H[p];
return ans;
}
int main(){
// freopen("earthworm.in","r",stdin);
// freopen("earthworm.out","w",stdout);
in(n),in(m),in(q),in(u),in(v),in(t);p=(LD)u/v;
for(Re i=1;i<=n;++i)in(a[i]),a[i]*=-1;
sort(a+1,a+n+1);
for(Re i=1;i<=n;++i)Q[0][++T[0]]=-a[i];//第一波蚯蚓入队
for(Re o=1;o<=m;++o){
Re x=Top()+tmp;//获取真实长度要加上懒标记
Re A=x*p,B=x-A;
if(o%t==0)printf("%d ",x);
tmp+=q;//修改懒标记
Q[1][++T[1]]=A-tmp;//入队时要减去懒标记
Q[2][++T[2]]=B-tmp;//同上
}
puts("");
for(Re o=1;o<=n+m;++o){
x=Top();
if(o%t==0)printf("%d ",x+tmp);//输出真实长度
}
fclose(stdin);
fclose(stdout);
return 0;
}
【T3】
【题目描述】
分别给出平面上 \(n\) \((n \leqslant 18)\) 只小猪的坐标 \((x_{i},y_{i})\) \((0 < x,y < 10)\),有一架弹弓位于 \((0,0)\) 处,每次 \(\text{Kiana}\)(%%%)可以发射一只飞行轨迹为 \(y=ax^2+bx\) \((\)需满足 \(a<0)\) 的小鸟。
一共有 \(T\) \((T \leqslant 30)\) 组数据,求每组数据最少需要多少只小鸟才能打完所有小猪。
【分析】
感受到了来自出题人深深的恶意,以后都不敢直视这个游戏了。
\(n \leqslant 18\),不是 \(dfs\) 就是状压,事实证明两种思路都可以过。个人认为状压更好想,写起来也比较简单。
首先要 \(n^3\) 预处理出过任意两点 \(i,j\)(或只过一点)的抛物线可以穿过的小猪有哪些,并用一个整数 \(line[i][j]\) 来表示这个状态。注意特判:仅当求出的一元二次函数 \(a<0\) 时才合法。
用 \(dp[j]\) 表示小猪状态为 \(j\) 时的最小答案,则转移方程可表示为:\(dp[j|line[i][k]]=min\{dp[j|line[i][k]],dp[j]+1\}\) 。
注意:不能写 \(dp[j]=min\{dp[j\) ^ \(line[i][k]]+1\}\),因为一头猪是可以被多条抛物线覆盖的,而这种写法是不能互相覆盖的情况(但貌似数据水可以过)。
暴力枚举 \(i,j,k\),时间复杂度为:\(O(Tn^{2}2^{n})\) 。
对于这道题,我表示很 \(angry\),因为模拟考试时抽风成为常态的 \(Cena\) 又把我搞爆蛋了,而且爆的还是 \(long\) \(double\) 读入的 \(\color{red}{C++标准写法}\),真的是无语了。。。
【Code】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define LD long double
#define Re register int
using namespace std;
const int N=20,M=262150;
int n,T,V,HYJ,dp[M],line[N][N];LD eps=1e-10;//到底是eps还是esp呢?我说不清
struct Poi{LD x,y;inline bool operator<(Poi O)const{return x!=O.x?x<O.x:y<O.y;};}P[N];
struct Line{
LD a,b;
inline bool judge(Poi O)const{//判断某点是否在某抛物线上
LD x=a*O.x*O.x+b*O.x,y=O.y;
return (x-y>=-eps&&x-y<=eps);//比较大小时的精度处理
}
};
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline Line Get_line(Poi a,Poi b){//手推公式,初中数学
Line ans;
ans.a=(a.y/a.x-b.y/b.x)/(a.x-b.x);
ans.b=a.y/a.x-ans.a*a.x;
return ans;
}
inline void print(Re j){for(Re k=n;k>=1;--k)printf("%d",(j&(1<<k-1))>0);}
int main(){
// freopen("angrybirds.in","r",stdin);
// freopen("angrybirds.out","w",stdout);
in(T);
while(T--){
in(n),in(HYJ),V=(1<<n)-1;
for(Re i=1;i<=n;++i)scanf("%Lf%Lf",&P[i].x,&P[i].y);
//我偏要用scanf读long double,我不信你noi linux能卡标准写法
sort(P+1,P+n+1);
memset(line,0,sizeof(line));
for(Re i=1;i<=n;++i)line[i][i]=1<<i-1;//单独处理只经过一个点的抛物线
for(Re i=1;i<=n;++i)
for(Re j=1;j<i;++j){
Line L=Get_line(P[i],P[j]);
if(L.a>=0)continue;//题目要求a<0
for(Re k=1;k<=n;++k)
if(L.judge(P[k]))line[i][j]|=1<<k-1;
line[j][i]=line[i][j];
}
memset(dp,127,sizeof(dp));
dp[0]=0;
for(Re i=1;i<=n;++i)
for(Re j=0;j<=V;++j)
for(Re k=1;k<=n;++k)
dp[j|line[i][k]]=min(dp[j|line[i][k]],dp[j]+1);
printf("%d\n",dp[V]);
}
fclose(stdin);
fclose(stdout);
return 0;
}
【题解】NOIP2016提高组 复赛的更多相关文章
- [日记&做题记录]-Noip2016提高组复赛 倒数十天
写这篇博客的时候有点激动 为了让自己不颓 还是写写日记 存存模板 Nov.8 2016 今天早上买了两个蛋挞 吃了一个 然后就做数论(前天晚上还是想放弃数论 但是昨天被数论虐了 woc noip模拟赛 ...
- 破译情报-NOIP2016提高组复赛模拟试题
[题目描述] 最近国安人员截获了一份 RB 国的秘密情报, 全文都是经过加密的,每个单 词都很长.破译人员想到先把单词化简一下,方法是把每个单词尽量取短些的前 缀,但所取的前缀不能是其他单词的前缀. ...
- NOIP2016提高组复赛C 愤怒的小鸟
题目链接:http://uoj.ac/problem/265 题目大意: 太长了不想概括... 分析: 状压DP的模板题,把所有可能的抛物线用二进制表示,然后暴力枚举所有组合,详情见代码内注释 代码如 ...
- 【NOIP2016提高组复赛day2】天天爱跑步
题目 小 C 同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏. <天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一棵 ...
- 【题解】NOIP2015提高组 复赛
[题解]NOIP2015提高组 复赛 传送门: 神奇的幻方 \([P2615]\) 信息传递 \([P2661]\) 斗地主 \([P2668]\) 跳石头 \([P2678]\) 子串 \([P26 ...
- 【题解】NOIP2016 提高组 简要题解
[题解]NOIP2016 提高组 简要题解 玩具迷题(送分) 用异或实现 //@winlere #include<iostream> #include<cstdio> #inc ...
- NOIP2016提高组解题报告
NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合
- NOIP2016普及组复赛解题报告
提高组萌新,DAY1DAY2加起来骗分不到300,写写普及组的题目聊以自慰. (附:洛谷题目链接 T1:https://www.luogu.org/problem/show?pid=1909 T2:h ...
- [题解]noip2016普及组题解和心得
[前言] 感觉稍微有些滑稽吧,毕竟每次练的题都是提高组难度的,结果最后的主要任务是普及组抱一个一等奖回来.至于我的分数嘛..还是在你看完题解后写在[后记]里面.废话不多说,开始题解. 第一题可以说的内 ...
随机推荐
- 调试接口你还在用postman吗
作者 | 陈凯玲 来源 | my.oschina.net/keking/blog/3104972 接口调试是每个软件开发从业者必不可少的一项技能,一个项目的的完成,可能接口测试调试的时间比真正开发写代 ...
- 关于python的十一道练习
关于python的十一道练习 1.编写程序,输入一个自然数字符串,然后输出各位数字之和.例如,输入字符串1234,输出10. def sums1(): #第一题 strs=input('请输入一个自然 ...
- Vue实现简单的列表金额计算效果(简易购物车)
效果图: 使用技术:v-for v-bind v-on实现简单的列表选中绑定操作 代码: <!DOCTYPE html> <html> <head> <met ...
- wpf file embeded resource is readonly,Copy always will copy the file and its folder to the bin folder
Wpf file embeded resource will compile the file into the assembly and it will be readonly and can no ...
- Z从壹开始前后端分离【 .NET Core2.2/3.0 +Vue2.0 】框架之六 || API项目整体搭建 6.1 仓储+服务+抽象接口模式
本文梯子 本文3.0版本文章 前言 零.完成图中的粉色部分 2019-08-30:关于仓储的相关话题 一.创建实体Model数据层 二.设计仓储接口与其实现类 三.设计服务接口与其实现类 四.创建 C ...
- Python 内置函数补充匿名函数
Python3 匿名函数 定义一个函数与变量的定义非常相似,对于有名函数,必须通过变量名访问 def func(x,y,z=1): return x+y+z print(func(1,2,3)) 匿名 ...
- Python中最常见的10个问题(list)
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 大熊 PS:如有需要Python学习资料的小伙伴可以加点击下方链接 ...
- golang中type常用用法
golang中,type是非常重要的关键字,一般常见用法就是定义结构,接口等,但是type还有很多其它的用法,在学习中遇到了以下几种,这点简单总结记录下 定义结构 type Person struct ...
- Java技巧——比较两个日期相差的天数
Java技巧——比较两个日期相差的天数 摘要:本文主要记录了在Java里面如何判断两个日期相差的天数. 判断两个Date类型的日期之间的天数 通过计算毫秒数判断: public static void ...
- jQuery animate() 方法
定义和用法 animate() 方法执行 CSS 属性集的自定义动画. 该方法通过 CSS 样式将元素从一个状态改变为另一个状态.CSS属性值是逐渐改变的,这样就可以创建动画效果. 只有数字值可创建动 ...