Urozero Autumn 2016. BAPC 2016
A. Airport Logistics
根据光路最快原理以及斯涅尔定律,可以得到从定点$P$进入某条直线的最佳入射角。
求出每个端点到每条线段的最佳点,建图求最短路即可。
时间复杂度$O(n^2\log n)$。
#include<cstdio> #include<cmath> #include<algorithm> #include<vector> #include<queue> using namespace std; typedef pair<double,int>PI; const int N=41000,M=1000000; const double eps=1e-9,inf=1e100; int n,cnt,i,j,g[N],v[M],nxt[M],ed;double w[M],d[N],va,vb,si[2],co[2]; priority_queue<PI,vector<PI>,greater<PI> >q; inline void add(int x,int y,double z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} inline void add2(int x,int y,double z){add(x,y,z),add(y,x,z);} inline void ext(int x,double y){if(y+eps<d[x])q.push(PI(d[x]=y,x));} inline int sgn(double x){ if(x>eps)return 1; if(x<-eps)return -1; return 0; } struct P{ double x,y; P(){} P(double _x,double _y){x=_x,y=_y;} P operator+(P b){return P(x+b.x,y+b.y);} P operator-(P b){return P(x-b.x,y-b.y);} P operator*(double b){return P(x*b,y*b);} P operator/(double b){return P(x/b,y/b);} double operator*(P b){return x*b.x+y*b.y;} bool operator==(P b){return !sgn(x-b.x)&&!sgn(y-b.y);} double len(){return hypot(x,y);} P rotate(double s,double c){return P(x*c-y*s,x*s+y*c);} P rot90(){return P(-y,x);} }a[210]; struct E{ double x;int y; E(){} E(double _x,int _y){x=_x,y=_y;} }e[410]; inline bool cmp(const E&a,const E&b){return a.x<b.x;} inline double cross(P a,P b){return a.x*b.y-a.y*b.x;} inline bool point_on_segment(P p,P a,P b){ return sgn(cross(b-a,p-a))==0&&sgn((p-a)*(p-b))<=0; } inline P line_intersection(P a,P b,P p,P q){ double U=cross(p-a,q-p),D=cross(b-a,q-p); return a+(b-a)*(U/D); } inline void work(int st,int en){ int i,j,m=2; P A=a[st],B=a[en],C=(B-A).rot90(); e[1]=E(0,st),e[2]=E(C.len(),en); for(i=0;i<=(n<<1|1);i++)if(i!=st&&i!=en)for(j=0;j<2;j++){ P D=line_intersection(A,B,a[i],a[i]+C.rotate(si[j],co[j])); if(D==A||D==B)continue; if(!point_on_segment(D,A,B))continue; cnt++; add2(i,cnt,(a[i]-D).len()/vb); e[++m]=E((D-A).len(),cnt); } sort(e+1,e+m+1,cmp); for(i=1;i<m;i++)add(e[i].y,e[i+1].y,(e[i+1].x-e[i].x)/va); } int main(){ scanf("%lf%lf",&a[0].x,&a[0].y); scanf("%lf%lf",&a[1].x,&a[1].y); scanf("%d",&n); va=2,vb=1; for(i=1;i<=n;i++){ scanf("%lf%lf",&a[i<<1].x,&a[i<<1].y); scanf("%lf%lf",&a[i<<1|1].x,&a[i<<1|1].y); } si[0]=vb/va; co[0]=sqrt(1.0-si[0]*si[0]); si[1]=-si[0]; co[1]=co[0]; cnt=n<<1|1; for(i=0;i<=cnt;i++)for(j=0;j<i;j++)add2(i,j,(a[i]-a[j]).len()/vb); for(i=1;i<=n;i++)work(i<<1,i<<1|1); for(i=0;i<=cnt;i++)d[i]=inf; ext(0,0); while(!q.empty()){ PI t=q.top();q.pop(); if(t.first-eps>d[t.second])continue; for(i=g[t.second];i;i=nxt[i])ext(v[i],t.first+w[i]); } printf("%.10f",d[1]); }
B. Battle Simulation
按题意模拟即可。
#include<cstdio> #include<algorithm> #include<string.h> using namespace std; const int top = 1e6; const int N = top + 10, M = 262150; char s[N], ans[N]; int main(){ scanf("%s", s); int len = strlen(s); int num = 0; for(int i = 0; i < len; i ++){ if(i + 2 < len && s[i] != s[i + 1] && s[i] != s[i + 2] && s[i + 1] != s[i + 2]){ ans[++ num] = 'C'; i += 2; } else if(s[i] == 'R') ans[++ num] = 'S'; else if(s[i] == 'B') ans[++ num] = 'K'; else ans[++ num] = 'H'; } printf("%s\n", ans + 1); } /* 4 4 3 1 2 2 3 3 4 5 2 1 4 1 2 3 2 1 4 2 2 3 2 2 4 8 9 1 2 2 3 1 3 3 4 4 5 4 6 5 7 5 8 7 8 5 2 7 8 2 1 6 2 4 7 1 6 8 2 5 6 */
C. Brexit
拓扑排序,不断删掉不合法的点即可。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 3e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n, m, ME, ST; vector<int>a[N]; bool stay[N]; int ind[N]; int least[N]; int sta[N]; int main() { while(~scanf("%d%d%d%d", &n, &m, &ME, &ST)) { for(int i = 1; i <= n; ++i) { a[i].clear(); stay[i] = 1; ind[i] = 0; } for(int i = 1; i <= m; ++i) { int x, y; scanf("%d%d", &x, &y); a[x].push_back(y); a[y].push_back(x); ++ind[x]; ++ind[y]; } for(int i = 1; i <= n; ++i) { least[i] = ind[i] / 2 + 1; } int top = 0; sta[++top] = ST; stay[ST] = 0; while(top) { int x = sta[top--]; for(auto y : a[x]) if(stay[y]) { if(--ind[y] < least[y]) { sta[++top] = y; stay[y] = 0; } } } puts(stay[ME] ? "stay" : "leave"); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 4 3 4 1 2 3 2 4 1 2 5 5 1 1 3 4 1 2 2 3 1 3 2 5 4 5 3 1 1 2 1 3 2 3 2 4 3 4 10 14 1 10 1 2 1 3 1 4 2 5 3 5 4 5 5 6 5 7 5 8 5 9 6 10 7 10 8 10 9 10 */
D. Bridge Automation
设$f[i]$表示前$i$艘船开走的最小代价,枚举与$i$最早一起开走的船$j$转移即可。
时间复杂度$O(n^2)$。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 4040, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n; int t[N]; int f[N]; int main() { while(~scanf("%d", &n)) { for(int i = 1; i <= n; ++i) { scanf("%d", &t[i]); } MS(f, 63); f[1] = 0; for(int i = 1; i <= n; ++i) { //printf("%d\n", f[i]); int down = t[i] + 1800 - 60; //桥放下的时间是定的 int pre = t[i] + 1800 - 20; for(int j = i; j <= n; ++j) //[i, j]一起走 { pre = max(pre + 20, t[j]); //这艘船开始开走的最早时间 gmin(f[j + 1], f[i] + pre + 20 + 60 - down); } } printf("%d\n", f[n + 1]); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 2 100 200 3 100 200 2010 3 100 200 2100 */
E. Charles in Charge
二分答案,最短路检验。
时间复杂度$O(n\log^2n)$。
#include<cstdio> #include<queue> #include<vector> #include<algorithm> using namespace std; typedef long long ll; typedef pair<ll,int>P; const int N=10010,M=200010; const ll inf=1LL<<50; int n,m,rate,i,x,y,z,g[N],v[M],w[M],nxt[M],ed,l,r,mid,ans; ll mindis,d[N]; inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} ll dij(int lim){ int i; for(i=1;i<=n;i++)d[i]=inf; priority_queue<P,vector<P>,greater<P> >q; q.push(P(d[1]=0,1)); while(!q.empty()){ P t=q.top();q.pop(); if(d[t.second]>t.first)continue; for(i=g[t.second];i;i=nxt[i])if(w[i]<=lim&&d[v[i]]>t.first+w[i]) q.push(P(d[v[i]]=t.first+w[i],v[i])); } return d[n]; } int main(){ scanf("%d%d%d",&n,&m,&rate); rate+=100; for(i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); add(x,y,z),add(y,x,z); } mindis=dij(1000000000); mindis*=rate; //should / 100 l=1,r=1000000000; while(l<=r){ mid=(l+r)>>1; if(dij(mid)*100<=mindis)r=(ans=mid)-1;else l=mid+1; } printf("%d",ans); }
F. Endless Turning
模拟$30000$步,这其中必然存在循环节,找出循环节后最后零碎部分继续模拟即可。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; const double eps=1e-9; const int N=110; const int inf=100000000; long long T; int n,m,i,pos,nxtpos; char name[N][N]; int loop=inf; inline int sgn(double x){ if(x<-eps)return -1; if(x>eps)return 1; return 0; } struct P{ double x,y; P(){x=y=0;} P(double _x,double _y){x=_x,y=_y;} P operator+(P b){return P(x+b.x,y+b.y);} P operator-(P b){return P(x-b.x,y-b.y);} P operator*(double b){return P(x*b,y*b);} P operator/(double b){return P(x/b,y/b);} bool operator==(P b){return !sgn(x-b.x)&&!sgn(y-b.y);} bool operator!=(P b){return sgn(x-b.x)||sgn(y-b.y);} bool operator<(P b){ if(sgn(x-b.x))return x<b.x; return y<b.y; } double operator*(P v){return x*v.x+y*v.y;} double len(){return hypot(x,y);} double len_sqr(){return x*x+y*y;} }A,B,C,D,a[N],b[N]; struct E{ P x; int y,z; E(){} E(P _x,int _y,int _z){x=_x,y=_y,z=_z;} }e[100010]; inline bool cmp(E a,E b){ if(a.x!=b.x)return a.x<b.x; return a.y!=b.y?a.y<b.y:a.z<b.z; } inline double cross(P a,P b){return a.x*b.y-a.y*b.x;} inline bool point_on_segment(P p,P a,P b){ return sgn(cross(b-a,p-a))==0; } inline int line_intersection(P a,P b,P p,P q,P&o,double&t){ double U=cross(p-a,q-p); double D=cross(b-a,q-p); if(sgn(D)==0)return 0; o=a+(b-a)*(U/D); t=U/D; return 1; } int getpos(P o){ for(int i=1;i<=n;i++)if(point_on_segment(o,a[i],b[i]))return i; return 0; } inline bool go(){ int nxt=0;double dis=1e100; for(int i=1;i<=n;i++)if(i!=abs(pos)){ P o;double t; if(line_intersection(A,A+B,a[i],b[i],o,t)){ //printf("->%d %.8f %.8f %.8f\n",i,o.x,o.y,t); if(t<eps)continue; if(t<dis)dis=t,nxt=i; } } if(!nxt)return 0; line_intersection(A,A+B,a[nxt],b[nxt],C,dis); nxtpos=nxt; D=b[nxt]-a[nxt]; if(cross(C-A,C+D-A)>=0)nxtpos*=-1,D=D*(-1); return 1; } int main(){ scanf("%d%lld%lf%lf",&n,&T,&A.x,&A.y); for(i=1;i<=n;i++){ scanf("%s",name[i]); scanf("%lf%lf%lf%lf",&a[i].x,&a[i].y,&b[i].x,&b[i].y); } pos=getpos(A); if(T==0){ puts(name[abs(pos)]); return 0; } B=b[pos]-a[pos];//direction if(B.x<0)B=B*(-1),pos*=-1; //printf("st : (%.8f,%.8f)->(%.8f,%.8f)\n",A.x,A.y,A.x+B.x,A.y+B.y); while(T>0&&m<30000){ if(!go()){ puts(name[abs(pos)]); return 0; } A=C,B=D,pos=nxtpos; //printf("(%.8f,%.8f)->(%.8f,%.8f)\n",A.x,A.y,A.x+B.x,A.y+B.y); T--; m++; e[m]=E(A,pos,m); } if(T==0){ puts(name[abs(pos)]); return 0; } sort(e+1,e+m+1,cmp); for(i=1;i<m;i++)if(e[i].x==e[i+1].x&&e[i].y==e[i+1].y){ loop=min(loop,e[i+1].z-e[i].z); } T%=loop; //printf("loop=%d\n",loop); while(T>0){ go(); A=C,B=D,pos=nxtpos; //printf("(%.8f,%.8f)->(%.8f,%.8f)\n",A.x,A.y,A.x+B.x,A.y+B.y); T--; } puts(name[abs(pos)]); } /* 3 4 1 1 Broadway 0 0 0 1 Narrowlane 0 0 1 0 Homedrive 1 1 2 0 3 4 5 0 Broadway 0 0 0 1 Narrowlane 0 0 1 0 Homedrive 1 1 2 0 */
G. Manhattan Positioning System
将曼哈顿距离转切比雪夫距离,则可行解位于若干个正方形的交集上。
在对应正方形四个端点附近枚举所有点检查即可。
时间复杂度$O(n)$。
#include<cstdio> #include<algorithm> #include<cstdlib> #include<set> #include<algorithm> using namespace std; typedef long long ll; typedef pair<int,int>P; const ll inf=1LL<<60; int n,i; ll a[1010][3],xl,xr,yl,yr; ll retx,rety; set<P>ans; inline void check(ll X,ll Y){ ll x=(X+Y)/2,y=X-x; for(int i=1;i<=n;i++)if(abs(x-a[i][0])+abs(y-a[i][1])!=a[i][2])return; retx=x,rety=y; ans.insert(P(x,y)); } void go(ll x,ll y){ ll k=10; for(ll i=x-k;i<=x+k;i++)for(ll j=y-k;j<=y+k;j++)check(i,j); } int main(){ scanf("%d",&n); xl=-inf; xr=inf; yl=-inf; yr=inf; for(i=1;i<=n;i++){ ll x,y,d; scanf("%lld%lld%lld",&x,&y,&d); a[i][0]=x; a[i][1]=y; a[i][2]=d; ll X=x+y,Y=x-y; xl=max(xl,X-d); xr=min(xr,X+d); yl=max(yl,Y-d); yr=min(yr,Y+d); } go(xl,yl); go(xl,yr); go(xr,yl); go(xr,yr); if(ans.size()==0)puts("impossible"); else if(ans.size()==1)printf("%lld %lld",retx,rety); else puts("uncertain"); }
H. Multiplying Digits
最优解中从低位到高位每个数必定是不上升的,爆搜剪枝配合卡时即可通过。
#include<cstdio> #include<time.h> typedef unsigned long long ll; const int N = 11111; const ll inf = 1ULL << 63; ll K, n, i, a[N], ans = inf; int m; inline ll mul(ll a, ll b) { if (a>ans / b)return ans; return a*b; } int ED = 5.9 * CLOCKS_PER_SEC; inline bool cal(ll ret, ll x, ll base, ll now) { while (ret >= x) { now += mul(base, x); base = mul(base, K); if (now >= ans)return 0; ret /= x; } return 1; } void dfs(int x, ll base, ll ret, ll n) {//now consider a[x] //printf("%d %llu %llu %llu\n",x,base,ret,n); if (ret >= ans)return; if (clock() > ED)return; if (n == 1) { //printf("%llu\n",ret); ans = ret; return; } if (base >= ans)return; while (1) { while (x <= m&&n%a[x])x++; if (x>m)return; ll t = ret + mul(base, a[x]); if (t >= ans)return; if (!cal(n, a[x], base, ret))return; dfs(x, mul(base, K), t, n / a[x]); if (clock() > ED)return; x++; } } int main() { /* ll t=1; for(i=1;i<=18;i++)t*=6; printf("%llu\n",t);*/ scanf("%llu%llu", &K, &n); if (n == 1) { puts("1"); return 0; } for (i = K - 1; i >= 2; i--)if (n%i == 0) { a[++m] = i; } //for(i=1;i<=m;i++)printf("%llu ",a[i]);puts(""); //printf("%d\n",m); a[0] = a[m + 1] = 1; dfs(1, 1, 0, n); if (ans == inf)puts("impossible"); else printf("%llu", ans); return 0; } /* 10 24 10 11 9 216 10000 5810859769934419200 9 101559956668416 */
I. Older Brother
按题意模拟即可。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n; bool solve() { if(n == 1)return 0; for(int i = 2; i * i <= n; ++i)if(n % i == 0) { int x = n; while(x % i == 0) { x /= i; } return x == 1; } return 1; } int main() { while(~scanf("%d", &n)) { puts(solve() ? "yes" : "no"); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
J. Programming Tutors
二分答案,二分图匹配检验。
时间复杂度$O(n^3\log n)$。
#include<cstdio> const int N=110; int n,i,j,l,r,mid,ans,d[N][N],v[N],f[N]; struct P{int x,y;}a[N],b[N]; inline int abs(int x){return x>0?x:-x;} bool find(int x){ for(int i=1;i<=n;i++)if(d[x][i]<=mid&&!v[i]){ v[i]=1; if(!f[i]||find(f[i]))return f[i]=x,1; } return 0; } bool check(){ int i,j; for(j=1;j<=n;j++)f[j]=0; for(i=1;i<=n;i++){ for(j=1;j<=n;j++)v[j]=0; if(!find(i))return 0; } return 1; } int main(){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y); for(i=1;i<=n;i++)scanf("%d%d",&b[i].x,&b[i].y); for(i=1;i<=n;i++)for(j=1;j<=n;j++)d[i][j]=abs(a[i].x-b[j].x)+abs(a[i].y-b[j].y); l=0,r=1000000000; while(l<=r){ mid=(l+r)>>1; if(check())r=(ans=mid)-1;else l=mid+1; } printf("%d",ans); }
K. Safe Racing
设$f[i]$表示长度为$i$的序列,$1$和$i$必选时的合法方案数,可以通过前缀和$O(n)$求出。
枚举第一个放的位置,那么最后一个位置的范围是一个区间,同样可以前缀和加速。
时间复杂度$O(n)$。
#include<cstdio> const int N=1000010,P=123456789; int n,m,i,j,ans,f[N],s[N]; int main(){ scanf("%d%d",&n,&m); f[1]=s[1]=1; for(i=2;i<=n;i++){ f[i]=s[i-1]; if(i-m-1>=0)f[i]-=s[i-m-1]; f[i]=(f[i]+P)%P; s[i]=(s[i-1]+f[i])%P; } for(i=1;i<=m;i++){ j=n-m+i; if(j>n)continue; int l=j-i+1,r=n-i+1; ans=(ans+s[r])%P; ans=(ans-s[l-1]+P)%P; } printf("%d",ans); }
L. Sticky Situation
排序后检查相邻$3$项能否形成三角形即可。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 2e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n; LL a[N]; bool solve() { for(int i = 1; i <= n - 2; ++i) { if(a[i] + a[i + 1] > a[i + 2])return 1; } return 0; } int main() { while(~scanf("%d", &n)) { for(int i = 1; i <= n; ++i) { scanf("%lld", &a[i]); } sort(a + 1, a + n + 1); puts(solve() ? "possible" : "impossible"); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */