[3.9校内训练赛]
Posted FallDream
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[3.9校内训练赛]相关的知识,希望对你有一定的参考价值。
不要纠结为什么3.9的比赛比3.10的还迟做完,你们首先得知道这场是ditoly出的
ditoly丧病出题人啊丧病出题人啊丧病出题人啊
本机上 T1 std3.000s卡过,我的3.1s被卡了 T3 总共2s,std写的treap跑的飞快,我写的替罪羊2.1s也被卡了
听说学校机子T1只要2s,学校机子真神奇,MBA还是不适合跑程序,低压i5伤不起啊......
分割线------------------------------------
A.一个n*m的网格图,每个格子都有一个0-9的权值,求(1,1)到(n,m)的最短路径。n,m<=5000,数据随机生成。
不会做,交了个spfa上去只有50,一个学弟写的dij过了70,真的牛逼。
题解:把每个权值大于0的点拆成很多个权值为1的点,然后bfs,遇到0的点就往外先扩展。由于数据随机生成,复杂度期望为4.5*nm
发现直接用stl的队列和手写队列差了大概0.2s,反正本机就是跑不过去,假装已经在学校机子上A了。
#include<iostream> #include<cstdio> #define INF 2000000000 #define MX 15000000 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();} return x*f; } int n,m,xx,yy,nowx,nowy,top=0,tail=0; char s[5005][5005]; int d[5005][5005]; bool b[5005][5005]; short qx[MX+5],qy[MX+5]; const int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; void solve(int x,int y) { for(int i=0;i<4;i++) { xx=x+dis[i][0]; yy=y+dis[i][1]; if(b[xx][yy])continue; b[xx][yy]=1;d[xx][yy]=d[x][y]+s[xx][yy]-‘0‘; if(s[xx][yy]==‘0‘)solve(xx,yy); else qx[++top>MX?(top-=MX):top]=xx,qy[top]=yy; } } int main() { freopen("secret.in","r",stdin); freopen("secret.out","w",stdout); n=read();m=read(); for(int i=1;i<=n;i++) scanf("%s",s[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) d[i][j]=INF; d[1][1]=s[1][1]-‘0‘; qx[++top]=1;qy[top]=1;b[1][1]=1; for(int i=0;i<=max(n,m);i++) b[i][0]=b[0][i]=b[n+1][i]=b[i][m+1]=1; while(top!=tail&&d[n][m]==INF) { nowx=qx[++tail>MX?(tail-=MX):tail];nowy=qy[tail]; if(s[nowx][nowy]>‘0‘) --s[nowx][nowy]; if(s[nowx][nowy]==‘0‘) solve(nowx,nowy); else qx[++top>MX?(top-=MX):top]=nowx,qy[top]=nowy; } printf("%d\n",d[n][m]); return 0; }
T2.k组询问,每次给定n,求∑f(i)/i 2<=i<=n,其中f(i)表示i能够分为多少种完全k次方数。 保留8位小数,差值不超过10-8判对, n<=10^36 k<=100000
std做法:用pq对10^12暴力,这时候答案0.9999998几,然后发现答案不会超过1,剩下的打表。
我的做法: 枚举次数(最多50),然后暴力枚举底数直到结果超过10^15,这时候已经0.99999997,打表最后几个数。我以为是输出答案相差不超过10-8,就输出0.99999999,结果是正确答案和你的输出答案不超过10-8,被坑了,100000组询问错了几十组,真的难受。实际上再二分一下就能过了,但我懒,也不想改了。
我的复杂度是 MAXN=10^15 log*(MAXN^0.5+MAXN^0.33+...+MAXN^0.02) log是快速幂的小log,复杂度非常科学,但就输在了打表上。
80/100,打的小表是每个次数的底数最大值,真正的打表部分处理在输入那里,需要自己二分一个区间。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define MAXN 1000000000000000LL #define ll long long using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();} return x*f; } double ad[100005]; double ts[100005]; double ans[100005]; char st[50]; const int lim[51]={0,0,31622776,100000,5623,1000,316,138,74,46,31,23,17,14,11,10,8,7,6,6,5,5,4,4,4,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}; int t; ll sum; ll pow(int x,ll p) { ll sum=1; for(ll i=x;p;p>>=1,i*=i)if(p&1) sum*=i; return sum; } struct node{ ll x;int num; }s[100005]; bool cmp(node x,node y){return x.x<y.x;} int main() { freopen("password.in","r",stdin); freopen("password.out","w",stdout); t=read(); for(int i=1;i<=t;i++) { scanf("%s",st+1);int len=strlen(st+1); if(len>18) s[i].x=MAXN,ad[i]=0.00000003; else if(len>15) s[i].x=MAXN,ad[i]=0.00000002; else for(int j=1;st[j];j++) s[i].x=s[i].x*10+st[j]-‘0‘; s[i].num=i; } sort(s+1,s+t+1,cmp); for(int l=2;l<50;++l) { memset(ts,0,sizeof(ts)); for(int i=2,j=1;i<=lim[l]&&j<=t;i++) { sum=pow(i,l); for(;s[j].x<sum&&j<=t;++j); if(j<=t) ts[j]+=(double)1/sum; } for(int i=1;i<=t;i++) ans[i]+=(ts[i]=ts[i-1]+ts[i]); } for(int i=1;i<=t;i++) ad[s[i].num]+=ans[i]; for(int i=1;i<=t;i++) printf("%0.8lf\n",ad[i]); return 0; }
C.给定一个长度为n的数列,每个数都是1-n以内。m次询问,每次询问一个区间,再给定s和k个下标。如果区间内有一个数出现超过区间长度一半,答案是那个数,否则答案是s,然后把k个下标的位置的数字改成这次的答案。求每一次的答案和最后整个数列的答案。 n,m<=500000,∑k <=1000000
有个学长用了随机化A了,结果被某个无良出题人临时搞数据卡了40.
做法:如果只考虑一个区间求有没有出现那么多次的人,我们可以采用一个很经典的做法。先记下第一个数和此时的出现次数(一开始是1),然后一个个往后处理,如果这个数和现在几下的数不同,那么出现次数-1,否则出现次数+1。如果出现次数变为0,则把记下的数字改成现在这个数,这样一定能找到那个数。
比如3 2 4 3 3 (3,1)->(2,0)->(4,1)->(3,0)->(3,1),找到了数字3
我们发现这个操作满足区间加法,所以可以用线段树来维护,每次从区间中找到那个数字。
可是如果没有出现次数大于一半的数字怎么办?很简单,我们可以用一棵平衡树来维护每个数字的出现位置,去里面查询一下就好啦。复杂度(n+k)logn
常数大没有考虑splay,就写了替罪羊,讲道理也不慢,但是就是在本机被卡了。std写的treap跑的飞快,有空学学。
#include<iostream> #include<cstdio> #define MAXN 1500000 #define N 524288 using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();} return x*f; } bool b[MAXN+5]; int fa[MAXN+5],size[MAXN+5],c[MAXN+5][2],nn[MAXN+5],q[MAXN+5],s[MAXN+5],col[MAXN+5]; int rt[500005],mark=0,n,m,top,cnt=0; struct data{ int x,num; data operator+(data y){if(!num)return y;if(!y.num)return *this;if(x==y.x)return(data){x,num+y.num}; if(num>y.num)return (data){x,num-y.num};return(data){y.x,y.num-num};} }T[N*2+5]; void renew(int x,int ad) { T[x+=N].x=ad; for(x>>=1;x;x>>=1) T[x]=T[x<<1]+T[x<<1|1]; } data query(int l,int r) { data sum=(data){0,0}; for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1) { if(~l&1)sum=sum+T[l+1]; if( r&1)sum=sum+T[r-1]; } return sum; } void init(){for(int i=N;i;i--)T[i]=T[i<<1]+T[i<<1|1];} void ins(int&x,int rk,int last) { //cout<<"ins"<<x<<" "<<rk<<" "<<last<<endl; if(!x){x=++cnt;size[x]=1;nn[x]=rk;fa[x]=last;b[x]=1;col[x]=s[rk];return;} if(rk<=nn[x])ins(c[x][0],rk,x);else ins(c[x][1],rk,x); size[x]=size[c[x][0]]+size[c[x][1]]+b[x]; if(max(size[c[x][0]],size[c[x][1]])>0.7*size[x]) mark=x; } void dfs(int x) { if(c[x][0])dfs(c[x][0]); if(b[x])q[++top]=x; if(c[x][1])dfs(c[x][1]); } void build(int&x,int l,int r,int last) { if(l>r)return;int mid=(l+r)>>1;x=q[mid];fa[x]=last; build(c[x][0],l,mid-1,x);build(c[x][1],mid+1,r,x); size[x]=size[c[x][0]]+size[c[x][1]]+b[x]; } void rebuild(int x) { top=mark=0;dfs(x);int y=fa[x]; for(int i=1;i<=top;i++)fa[q[i]]=c[q[i]][0]=c[q[i]][1]=0; if(!y)build(rt[col[x]],1,top,0); else build(c[y][c[y][1]==x],1,top,y); } void del(int x,int k) { // cout<<"del"<<x<<" "<<k<<endl; if(nn[x]==k&&b[x]){b[x]=0;size[x]--;return;} if(k<=nn[x])del(c[x][0],k);else del(c[x][1],k); size[x]=size[c[x][0]]+size[c[x][1]]+b[x]; } int query2(int x,int r) { //cout<<"query2"<<x<<" "<<r<<" "<<nn[x]<<endl; if(!x)return 0; if(nn[x]>r)return query2(c[x][0],r); else return size[c[x][0]]+b[x]+query2(c[x][1],r); } void solve(int l,int r,int q) { data xx=query(l,r);if(xx.num&&query2(rt[xx.x],r)-query2(rt[xx.x],l-1)>=(r-l+3)/2) q=xx.x; // cout<<xx.x<<" "<<query2(rt[xx.x],r)-query2(rt[xx.x],l-1)<<"!!"<<endl; printf("%d\n",q);if(!m)return;int k=read(); for(int j=1;j<=k;j++) {int x=read();if(s[x]!=q){renew(x,q);del(rt[s[x]],x);s[x]=q;ins(rt[q],x,0);if(mark)rebuild(mark);}} } int main() { freopen("president.in","r",stdin); freopen("president.out","w",stdout); n=read();m=read(); for(int i=1;i<=n;i++) {s[i]=read();T[i+N]=(data){s[i],1};ins(rt[s[i]],i,0);if(mark)rebuild(mark);}init(); while(m--){int l=read(),r=read(),q=read();solve(l,r,q);} solve(1,n,-1); return 0; }
丧病出题人丧病出题人!!!!!!!!!!!
以上是关于[3.9校内训练赛]的主要内容,如果未能解决你的问题,请参考以下文章