联赛前一天,作为强省的弱OIer,第一次参加Noip提高组还是挺紧张的。于是在训练的小黑屋了浪了一整天……
Day1
因为本土作战,而且家离学校近,所以睡到7:40才起床。
T1 模拟暴力,考试的时候竟然没注意到mogician……
T2 由于做了前几年的联赛,感觉前两道题应该挺水的,结果愣是看了一个多小时想不出正解……只好打80分的暴力。
T3 正解打挂没时间,搜索暴力……
Day2
T1 O(N2) 预处理后 O(1) 输出
T2 吸取第一天的教训,第二题直接STL堆暴力
T3 打完暴搜后还有一个多小时,开始着手优化搜索……在想记忆化的时候猛然想到可以状压DP……结果被卡精度,又没预处理… O(T∗2N∗N3)
听说零点出成绩,于是兴致冲冲地等到了0:00,结果CCF没上班,网页只有一个空壳…..
下午跑到机房,果然出成绩了……
分数线330……
直接暴力模拟。 O(N)
#include
#include
#include
#include
#define N 100010
using namespace std;
int n,f[N],m,noww;
char namE[N][20];
void reaD(int &x){
char Ch=getchar();x=0;
for(;Ch>'9'||Ch<'0';Ch=getchar());
for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}
void reaDs(char a[]){
char Ch=getchar();int len=0;
for(;Ch>'z'||Ch<'a';Ch=getchar());
for(;Ch>='a'&&Ch<='z';a[++len]=Ch,Ch=getchar());
a[++len]=0;
}
void M(int &noww){
if(noww<=0) noww+=n;
if(noww>n) noww-=n;
}
int main(){
reaD(n);reaD(m);
for(int i=1;i<=n;i++)
reaD(f[i]),reaDs(namE[i]);
noww=1;
for(int i=1,LoR,Lo;i<=m;i++){
reaD(LoR);reaD(Lo);
if(f[noww]^LoR) noww+=Lo;
else noww-=Lo;
M(noww);
}
for(int i=1;namE[noww][i]!=0;i++) putchar(namE[noww][i]);
return 0;
}
根据提供的数据信息可拿80……
对于一个从u到v的人,令 f 为 u , v 的Lca, d(x) 为 x 的深度, F(x) 为 x 的父节点
可以先考虑所有人都从根节点出发和所有人都跑到根节点的做法:
对于一个 T 时刻从 x 节点跑到根节点的人,如果路径上的观察员 i 满足 T+d(x)−d(i)=w(i) ,即 T+d[x]=w[i]+d[i] , 那么观察员 i 可以观察到这个人。
对于一个 T 时刻从根节点跑到 x 节点的人,如果路径上的观察员 i 满足 T+d(i)=w(i) ,即 T=w(i)−d(i) ,那么观察员 i 就可以观察到这个人
所以我们可以把一个从 u 到 v 的人分解:
但是这样的话 F(f) -根节点这条路径上的观察员也会观察到这个人,所以可以增加两个玩家:
然后按照所有人从根节点出发和所有人跑到根节点的方法做。
那么复杂度就集中在求LCA的过程,用树剖或倍增
O(NlogN+N+M)
#include
#define N 300010
int En,n,m;
int w[N],G[N],Ans[N],F[N],S[N],L[N],GG[N],tt;
int Top[N],ws[N],Dept[N],B[N],R[N],mi[N],GG1[N];
struct timble{
int t,nx,w;
}Tb[N<<3];
struct edgee{
int t,nx;
}E[N<<1];
inline void reaD(int &x){
char Ch=getchar();x=0;
for(;Ch>'9'||Ch<'0';Ch=getchar());
for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}
inline void InserT(int x,int y){
E[++En].t=y;
E[En].nx=G[x];
G[x]=En;
}
inline void clf(){fclose(stdin);fclose(stdout);}
void D1(int x,int f,int d){
F[x]=f;S[x]=1;
for(int i=G[x];i;i=E[i].nx)
if(E[i].t!=f){
D1(E[i].t,x,d+1);
if(S[E[i].t]>S[ws[x]]) ws[x]=E[i].t;
S[x]+=S[E[i].t];
}
}
void D2(int x,int dep,int tp){
Dept[x]=dep;Top[x]=tp;
if(ws[x]) D2(ws[x],dep+1,tp);
for(int i=G[x];i;i=E[i].nx)
if(E[i].t!=F[x]&&E[i].t!=ws[x]) D2(E[i].t,dep+1,E[i].t);
}
inline void reaDedge(){
for(int i=1,x,y;ifor(int i=1;i<=n;i++) reaD(w[i]);
D1(1,0,1);D2(1,1,1);
}
inline int Lca(int x,int y){
int tp1,tp2;
while(Top[x]!=Top[y])
if(Dept[Top[x]]else x=F[Top[x]];
if(Dept[x]return x;
return y;
}
inline void InsertT2(int x,int t,int w){
Tb[++tt].nx=GG[x];
GG[x]=tt;
Tb[tt].t=t;
Tb[tt].w=w;
}
inline void InsertT1(int x,int t,int w){
Tb[++tt].nx=GG1[x];
GG1[x]=tt;
Tb[tt].t=t;
Tb[tt].w=w;
}
void S1(int x,int f){
int r=B[Dept[x]+w[x]];
for(int i=GG1[x];i;i=Tb[i].nx)
B[Tb[i].t]+=Tb[i].w;
for(int i=G[x];i;i=E[i].nx)
if(E[i].t!=f) S1(E[i].t,x);
Ans[x]+=B[Dept[x]+w[x]]-r;
}
int S2(int x,int f){
int r;
if(w[x]-Dept[x]>=0) r=R[w[x]-Dept[x]]; else r=mi[Dept[x]-w[x]];
for(int i=GG[x];i;i=Tb[i].nx)
if(Tb[i].t>=0)R[Tb[i].t]+=Tb[i].w; else mi[-Tb[i].t]+=Tb[i].w;
for(int i=G[x];i;i=E[i].nx)
if(E[i].t!=f) S2(E[i].t,x);
if(w[x]-Dept[x]>=0) Ans[x]+=R[w[x]-Dept[x]]-r;
else Ans[x]+=mi[Dept[x]-w[x]]-r;
}
int ww[20],wt;
inline void Pt(int x){
if(!x){putchar('0');return;}
while(x)ww[++wt]=x%10,x/=10;
for(;wt;wt--)putchar(ww[wt]+'0');
}
int main(){
reaD(n);reaD(m);
reaDedge();
for(int i=1,u,v,f;i<=m;i++){
reaD(u);reaD(v);
f=Lca(u,v);
InsertT1(u,Dept[u],1);
InsertT1(F[f],Dept[u],-1);
InsertT2(f,Dept[u]-Dept[f]*2,-1);
InsertT2(v,Dept[u]-2*Dept[f],1);
}
S1(1,0);S2(1,0);
for(int i=1;iputchar(' ');
return Pt(Ans[n]),0;
}
概率DP
f[i][j][k] 表示前 i 节课,申请了换 j 节课, k=1 表示第i节课申请了换课, k=0 表示没有。
g(i,j) 表示 i 教室与 j 教室的最短距离
那么对于第 i 节课,如果不申请换课:
如果申请换课,同意按照上面的方法分类,只不过要同时考虑第 i 节课和第 i−1 节课的申请成功的情况。
计算两个教室间的最短路程,因为教室数比较小,可以floyd
O(v3+nm)
#include
#include
#include
#include
#include
#include
#define N 2010
#define M 310
using namespace std;
typedef long long ll;
int n,m,v,e,C[N],D[N],w[N];
double K[N],Ans,f[2][N][2];
ll d[M][M];
inline void reaD(int &x){
char Ch=getchar();x=0;
for(;Ch>'9'||Ch<'0';Ch=getchar());
for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}
inline int min(const int &a,const int &b){
return ainline double min(double a,double b,double c){
return min(min(a,b),c);
}
int main(){
reaD(n);reaD(m);reaD(v);reaD(e);
for(int i=1;i<=v;i++){
for(int j=1;j<=v;j++) d[i][j]=1<<30;
d[i][i]=0;
}
for(int i=1;i<=n;i++) reaD(C[i]);
for(int i=1;i<=n;i++) reaD(D[i]);
for(int i=1;i<=n;i++){
scanf("%lf",&K[i]);
}
for(int i=1,x,y,z;i<=e;i++){
reaD(x);reaD(y);reaD(z);
d[x][y]=d[y][x]=min(d[x][y],z);
}
for(int k=1;k<=v;k++)
for(int i=1;i<=v;i++)
for(int j=1;j<=v;j++)
if(d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j];
memset(f,0x7F,sizeof(f));
f[1][1][1]=f[1][0][0]=0;
for(int i=2;i<=n;i++)
for(int j=0;j<=i&&j<=m;j++){
f[i&1][j][0]=min(f[i&1^1][j][0]+d[C[i-1]][C[i]],f[i&1^1][j][1]+d[D[i-1]][C[i]]*K[i-1]+d[C[i-1]][C[i]]*(1-K[i-1]));
if(j) f[i&1][j][1]=min(f[i&1^1][j-1][0]+d[C[i-1]][D[i]]*K[i]+d[C[i-1]][C[i]]*(1-K[i]),
f[i&1^1][j-1][1]+d[D[i-1]][D[i]]*K[i]*K[i-1]+d[D[i-1]][C[i]]*K[i-1]*(1-K[i])+d[C[i-1]][D[i]]*K[i]*(1-K[i-1])+d[C[i-1]][C[i]]*(1-K[i])*(1-K[i-1]));
}
Ans=1<<30;
for(int i=0;i<=m;i++) Ans=min(Ans,f[n&1][i][0],f[n&1][i][1]);
printf("%.2lf",Ans);
return 0;
}
根据 C(i,j)=C(i−1,j−1)+C(i,j−1) 预处理后输出
但是因为 C(i,j) 会很大,所已预处理的时候就可以对 C(i,j) 取模,之后判断是不是为零就行了
O(n2+T)
#include
#define N 2010
int A[N][N],n,m,k,t,Ans[N][N];
void reaD(int &x){
char Ch=getchar();x=0;
for(;Ch>'9'||Ch<'0';Ch=getchar());
for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}
int w[20],wt;
void Pt(int x){
if(!x){putchar('0');putchar('\n');return ;}
wt=0;while(x)w[++wt]=x%10,x/=10;
for(;wt;wt--)putchar(w[wt]+'0');putchar('\n');
}
int main(){
reaD(t);reaD(k);
for(int i=0;i<=2000;i++) A[i][0]=1;A[1][1]=1;
for(int i=2;i<=2000;i++){
for(int j=1;j<=i;j++){
if((A[i][j]=A[i-1][j-1]+A[i-1][j])>=k) A[i][j]-=k;
}
}
for(int i=1;i<=2000;i++)for(int j=i+1;j<=2000;j++) A[i][j]=-1;
for(int i=1;i<=2000;i++)
for(int j=1;j<=2000;j++){
Ans[i][j]=Ans[i-1][j]+Ans[i][j-1]-Ans[i-1][j-1]+(A[i][j]==0);
}
while(t--){
reaD(n);reaD(m);
Pt(Ans[n][m]);
}
return 0;
}
可以开三个队列,分别记录初始的蚯蚓和切开后的两断蚯蚓的长度,因为存储初始蚯蚓的队列按照升序排列,所以可以证明另外两个队列也是升序的。
那么每次找最长的只要比较三个队列的头部就行了。
至于怎么处理每个单位时间增加的长度,可以开个变量L,L每个单位时间加上q,新增的蚯蚓的长度只要减去q就行了。
O(nlogn+m)
#include
#include
#include
#include
#include
#define N 100010
#define M 7000010
using namespace std;
int n,t,m,L,u,v,q,A[N],tt,T;
int B[M],C[M],lb,lc,tb,tc;
inline char NC(){
static char buf[100000],*p1=buf,*p2=buf;
if(p2==p1){
p2=(p1=buf)+fread(buf,1,100000,stdin);
if(p1==p2) return EOF;
}
return *p1++;
}
void reaD(int &x){
char Ch=NC();x=0;
for(;Ch>'9'||Ch<'0';Ch=NC());
for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=NC());
}
int w[20],wt;
void Pt(int x){
if(!x){putchar('0');return ;}
wt=0;while(x)w[++wt]=x%10,x/=10;
for(;wt;wt--)putchar(w[wt]+'0');
}
inline void Cut(int x,int ti){
if(ti==tt){
Pt(x+L),tt+=T;
if(tt<=m) putchar(' ');
}
int a=1ll*(x+L)*u/v,b=x+L-a;L+=q;
B[++tb]=a-L;C[++tc]=b-L;
}
inline void pt(int x,int ti){
if(ti==tt){
Pt(x),tt+=T;
if(tt<=m+n) putchar(' ');
}
}
inline bool cmp(const int &a,const int &b){
return a>b;
}
int main(){
reaD(n);reaD(m);reaD(q);reaD(u);reaD(v);reaD(T);tt=T;
for(int i=1;i<=n;i++) reaD(A[i]);
sort(A+1,A+1+n,cmp);lb=lc=t=1;
for(int i=1;i<=m;i++){
if(t<=n&&(A[t]>=B[lb]||lb>tb)&&(A[t]>=C[lc]||lc>tc)) Cut(A[t++],i);
else if(lb<=tb&&(B[lb]>=C[lc]||lc>tc)) Cut(B[lb++],i);
else Cut(C[lc++],i);
}putchar('\n');
int i=1;tt=T;
while(t<=n||lb<=tb||lc<=tc){
if(t<=n&&(A[t]>=B[lb]||lb>tb)&&(A[t]>=C[lc]||lc>tc)) pt(A[t++]+L,i);
else if(lb<=tb&&(B[lb]>=C[lc]||lc>tc)) pt(B[lb++]+L,i);
else pt(C[lc++]+L,i);
i++;
}
return 0;
}
状压DP
因为只有18只小鸟,可以把状态压缩在2^18的整数内。
但是这样DP的复杂度就是 O(2nn3)
所以我们可以枚举两只小鸟,计算以他们的坐标和原点坐标画成的抛物线上有哪些小鸟,也用2^18的整数记录下来。
这样复杂度就是 O(2nn2) 了
总复杂度 0(2nn2∗T)
#include
#include
#include
#include
using namespace std;
int n,m,l,r,mid,v[20],qt,tt,d[20][20],Res[1<<20],w;
double x[20],y[20];
struct prs{
double a1,b1,a2,b2;
}q[30],p[20][20];
double abs(double x){
if(x<0) return -x;
return x;
}
prs Biu(int l,int s){
prs Re;
Re.a1=(y[l]*x[s]-y[s]*x[l]),Re.a2=(x[l]*x[l]*x[s]-x[s]*x[s]*x[l]);
if(Re.a2==0) return Re;
Re.b1=(y[l]-Re.a1*x[l]*x[l]/Re.a2),Re.b2=x[l];
return Re;
}
void work(){
scanf("%d%d",&n,&m);qt=0;
for(int i=1;i<=n;i++) scanf("%lf %lf",&x[i],&y[i]),v[i]=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(x[i]!=x[j]){
p[i][j]=Biu(i,j);
d[i][j]=0;
if(p[i][j].a1*p[i][j].a2>-1e-6) continue;
for(int k=1;k<=n;k++)
if(abs(p[i][j].a1*x[k]*x[k]/p[i][j].a2+p[i][j].b1*x[k]/p[i][j].b2-y[k])<=1e-6) d[i][j]|=1<1;
}
memset(Res,0x7F,sizeof(Res));
Res[0]=0;
for(int i=0,sx;i<=(1<for(int j=1;j<=n;j++){
Res[i|(1<<(j-1))]=min(Res[i|(1<<(j-1))],Res[i]+1);
}
for(int j=1;j<=n;j++)
if((i&(1<<(j-1)))==0)
for(int k=j+1;k<=n;k++)
if(x[j]!=x[k]&&(i&(1<<(k-1)))==0&&p[j][k].a1*p[j][k].a2<0){
Res[i|d[j][k]]=min(Res[i|d[j][k]],Res[i]+1);
}
}
printf("%d\n",Res[(1<1]);
}
int main(){
int t;
scanf("%d",&t);
while(t--) work();
return 0;
}