A. Secret of Chocolate Poles
DP,$f[i][j]$表示高度為$i$,頂層顏色為$j$的方案數。
時間複雜度$O(l)$。
#include<cstdio> typedef __int128 lll; const int N=200; lll f[N][2],ans;//dark white int l,k,i; void write(lll x){ if(x>=10)write(x/10); x%=10; printf("%d",(int)(x)); } int main(){ scanf("%d%d",&l,&k); f[1][0]++; f[k][0]++; for(i=1;i<=l;i++){ f[i+1][1]+=f[i][0]; f[i+1][0]+=f[i][1]; f[i+k][0]+=f[i][1]; } for(i=1;i<=l;i++)ans+=f[i][0]; write(ans); }
B. Parallel Lines
將$O(n^2)$對點對按斜率分組,設$f[S][i]$表示$S$集合的點已經配對,當前分組選了$i$條直線的最大得分。
時間複雜度$O(2^nn^3)$。
#include<cstdio> #include<cmath> #include<cstdlib> #include<algorithm> using namespace std; const int N=16,inf=100000000; int n,i,j,m,mx,ans,f[1<<N][10],g[1<<N][10],w[N]; struct P{ int x,y; }a[N]; struct E{ int x,y,s; }e[N*N*5]; inline bool cmp(const E&a,const E&b){ if(a.x!=b.x)return a.x<b.x; return a.y<b.y; } inline void up(int&a,int b){a<b?(a=b):0;} inline void clr(){ for(int i=0;i<1<<n;i++)for(int j=0;j<=mx;j++)g[i][j]=f[i][j]; } inline void nxt(){ for(int i=0;i<1<<n;i++)for(int j=0;j<=mx;j++)f[i][j]=g[i][j]; } inline void ins(int S){ for(int i=0;i<1<<n;i++)if(!(i&S))for(int j=0;j<=mx;j++)if(f[i][j]>=0){ up(g[i|S][j+1],f[i][j]); } } inline void mov(){ for(int i=0;i<1<<n;i++)for(int j=0;j<=mx;j++)g[i][j]=-inf; for(int i=0;i<1<<n;i++)for(int j=0;j<=mx;j++)if(f[i][j]>=0)up(g[i][0],f[i][j]+w[j]); nxt(); } int main(){ for(i=0;i<N;i++)w[i]=i*(i-1)/2; scanf("%d",&n); mx=n/2; for(i=0;i<n;i++)scanf("%d%d",&a[i].x,&a[i].y); for(i=0;i<n;i++)for(j=0;j<i;j++){ int dx=a[i].x-a[j].x; int dy=a[i].y-a[j].y; int A,B; if(dx==0)A=0,B=1; else if(dy==0)A=1,B=0; else{ if(dx<0)dx*=-1,dy*=-1; int d=__gcd(abs(dx),abs(dy)); A=dx/d,B=dy/d; } e[++m].x=A; e[m].y=B; e[m].s=(1<<i)|(1<<j); } for(i=0;i<1<<n;i++)for(j=0;j<=mx;j++)f[i][j]=-inf; f[0][0]=0; sort(e+1,e+m+1,cmp); for(i=1;i<=m;i=j){ for(j=i;j<=m&&e[i].x==e[j].x&&e[i].y==e[j].y;j++){ clr(); ins(e[j].s); nxt(); } mov(); } for(i=0;i<1<<n;i++)up(ans,f[i][0]); printf("%d",ans); }
C. Medical Checkup
若上一個人比這一個人慢,則可以將兩人合併,否則可以獨立計算。
#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() { } #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 = 1e5 + 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, T; LL h[N], a[N], b[N]; int main() { while(~scanf("%d%d",&n, &T)) { for(int i = 1; i <= n; ++i)scanf("%lld", &h[i]); a[0] = b[0] = 0; for(int i = 1; i <= n; ++i) { if(b[i - 1] >= h[i]) { a[i] = a[i - 1] + h[i]; b[i] = b[i - 1]; } else { a[i] = a[i - 1] + h[i]; b[i] = h[i]; } } for(int i = 1; i <= n; ++i) { if(T < a[i]) { printf("%d\n", 1); } else { int t = T - a[i]; int g = t / b[i]; printf("%d\n", g + 2); } } } return 0; } /* 【trick&&吐槽】 【題意】 【分析】 【時間複雜度&&優化】 */
D. Making Perimeter of the Convex Hull Shortest
留坑。
E. Black or White
最優解中填充的區間一定是包含的或者相離的。
底層那次填充滿足長度不超過$k$後,內部填充不需要考慮長度限制。
此時內部最優解即為對應顏色連續段的個數,設$f[i]$表示$[1,i]$合法的最小代價,若$a[i]==b[i]$,則$f[i]=f[i-1]$;否則$f[i]=\min(f[j]+cost(j+1,i))+1$,其中$i-j\leq k$。
將$cost$用字首和表示,單調佇列優化轉移即可。
時間複雜度$O(n)$。
#include<cstdio> const int N=500010,inf=1000000000; int n,k,i,j,f[N],vr[N][2],vl[N][2],s[N];char a[N],b[N]; inline void up(int&a,int b){a>b?(a=b):0;} struct DS{ int h,t,a[N],b[N]; void init(){ h=t=1; } int ask(int x){ while(h<=t&&x-a[h]>k)h++; return b[h]; } void add(int x,int y){ while(h<=t&&b[t]>=y)t--; a[++t]=x; b[t]=y; } }q[2]; int main(){ scanf("%d%d%s%s",&n,&k,a+1,b+1); for(i=0;i<2;i++){ char o=i?'B':'W'; for(j=1;j<=n;j++){ s[j]=s[j-1]; if(b[j]==o&&b[j]!=b[j-1])s[j]++; vr[j][i]=s[j]; vl[j][i]=-s[j]+(b[j]==o&&b[j+1]==o); } } for(j=0;j<2;j++)q[j].init(); for(i=1;i<=n;i++){ f[i]=inf; if(a[i]==b[i])f[i]=f[i-1]; for(j=0;j<2;j++)up(f[i],q[j].ask(i)+1+vr[i][j]); for(j=0;j<2;j++)q[j].add(i,f[i]+vl[i][j]); //printf("f[%d]=%d\n",i,f[i]); } printf("%d",f[n]); }
F. Pizza Delivery
求出$1$和$2$到每個點的最短路,並得到$1$到$2$的最短路DAG,以及$1$和$2$在DAG上到每個點的路徑條數。
對於一條邊,若是DAG中的邊,則若經過它的方案數為總方案數,最短路變長,否則不變;
若不是DAG中的邊,則若對應最短路加上邊權小於最短路,最短路變短,否則不變。
路徑條數可以取模判斷。
時間複雜度$O(m\log m)$。
#include<cstdio> #include<algorithm> #include<vector> #include<queue> using namespace std; typedef long long ll; typedef pair<ll,int>P; const ll inf=1LL<<60; const int N=100010,M=100010,MOD=1000000007; int n,m,i,e[M][3]; struct G{ int g[N],v[M],w[M],nxt[M],ed; ll d[N]; priority_queue<P,vector<P>,greater<P> >q; inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} inline void ext(int x,ll y){if(d[x]>y)q.push(P(d[x]=y,x));} void work(int S){ int i; for(i=1;i<=n;i++)d[i]=inf; ext(S,0); 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])ext(v[i],t.first+w[i]); } } }A,B; struct GG{ int g[N],v[M],nxt[M],ed,f[N],vis[N]; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} int dfs(int x){ if(vis[x])return f[x]; vis[x]=1; for(int i=g[x];i;i=nxt[i])f[x]=(f[x]+dfs(v[i]))%MOD; return f[x]; } void work(int S){ f[S]=1; vis[S]=1; for(int i=1;i<=n;i++)dfs(i); } }C,D; ll best; int main(){ scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); e[i][0]=x; e[i][1]=y; e[i][2]=z; A.add(x,y,z); B.add(y,x,z); } A.work(1); B.work(2); best=A.d[2]; for(i=1;i<=m;i++){ int x,y,z; x=e[i][0]; y=e[i][1]; z=e[i][2]; if(A.d[x]+z+B.d[y]==best){ C.add(y,x); D.add(x,y); } } C.work(1); D.work(2); for(i=1;i<=m;i++){ int x,y,z; x=e[i][0]; y=e[i][1]; z=e[i][2]; if(A.d[x]+z+B.d[y]==best){ if(1LL*C.f[x]*D.f[y]%MOD==C.f[2])puts("SAD"); else puts("SOSO"); }else{ if(A.d[y]+z+B.d[x]<best)puts("HAPPY"); else puts("SOSO"); } } }
G. Rendezvous on a Tetrahedron
將立體圖形展開成二維,直接求出終點座標,然後計算位於哪一面即可。
#include<cstdio> #include<cmath> #include<algorithm> #include<cstdlib> using namespace std; #define double double double R,eps=1e-5,T; const double PI = acos(-1.0); int cmp(double x) { if(fabs(x) < eps) return 0; return x > 0 ? 1 : -1; } double Sin(double th) { double ap = th * PI / 180; return sin(ap); } struct point { double x, y; }a[3]; double det(point a, point b) { return a.x * b.y - a.y * b.x; } const double sq3 = sqrt(3.0), sq3_2 = sq3 / 2; double area(point a, point b, point c) { return fabs(det(a, b) + det(b, c) + det(c, a)); } bool inTri(double x, double y, int xx, int yy) { point d; d.x = x; d.y = y; double area1 = area(d, a[0], a[1]) + area(d, a[1], a[2]) + area(d, a[2], a[0]); double area2 = area(a[0], a[1], a[2]); if(cmp(area1 - area2) == 0) return 1; //if(area(d, a[0], a[1]) + area(d, a[1], a[2]) + area(d, a[2], a[0]) == area(a[0], a[1], a[2])) return 1; /* if(xx == 3 && yy == 3){ printf("area1 = %.10f\n", area1); printf("area2 = %.10f\n", area2); printf("x = %.10f y = %.10f ", x, y); printf("xx = %d yy = %d\n", xx, yy); for(int i = 0; i < 3; i ++){ printf("a[] = %.10f %.10f\n", a[i].x, a[i].y); } } */ return 0; } int solve(double &th2, double &lp2) { double th, lp; th = th2; lp = lp2; double ax, xy, X, Y; if(th <= 30){ ax = lp * Sin(60 + th) / Sin(90); xy = lp * Sin(30 - th) / Sin(90); X = -xy; Y = -ax; } else{ ax = lp * Sin(60 + th) / Sin(90); xy = lp * Sin(th - 30) / Sin(90); X = xy; Y = -ax; } // X 是橫座標,Y是縱座標 for(int x = 1; x <= 40; x ++){ for(int y = (x - 1) * 2 + 1; y >= 1; y --){ if(x % 2 == 1 && y % 2 == 1){ a[0].x = (y + 1) / 2 - (x + 1) / 2; a[0].y = (x - 1) * - sq3_2; a[1].x = a[0].x - 0.5; a[1].y = a[0].y - sq3_2; a[2].x = a[0].x + 0.5; a[2].y = a[1].y; } else if(x % 2 == 1){ a[0].x = (y + 1) / 2 - (x + 1) / 2; a[0].y = (x - 1) * -sq3_2; a[1].x = a[0].x + 1; a[1].y = a[0].y; a[2].x = a[0].x + 0.5; a[2].y = a[0].y - sq3_2; } else if(y % 2 == 1){ a[0].x = (y + 1) / 2 - (x + 2) / 2 + 0.5; a[0].y = (x - 1) * -sq3_2; a[1].x = a[0].x - 0.5; a[1].y = a[0].y - sq3_2; a[2].x = a[0].x + 0.5; a[2].y = a[1].y; } else{ a[0].x = (y + 1) / 2 - (x + 2) / 2 + 0.5; a[0].y = (x - 1) * -sq3_2; a[1].x = a[0].x + 1; a[1].y = a[0].y; a[2].x = a[0].x + 0.5; a[2].y = a[0].y - sq3_2; } if(inTri(X, Y, x, y)){ /* printf("pos: %.10f %.10f ", X, Y); printf("rc: %d %d\n", x, y); for(int i = 0; i < 3; i ++){ printf("%.10f %.10f\n", a[i].x, a[i].y); } */ if(x % 2 == 1){ if(y % 4 == 1){ return 1; } else if(y % 4 == 2){ return 4; } else if(y % 4 == 3){ return 2; } return 3; } else{ if(y % 4 == 1){ return 3; } else if(y % 4 == 2){ return 2; } else if(y % 4 == 3){ return 4; } return 1; } } } } } char s1[3], s2[3]; double th1, lp1, th2, lp2; void fre() { freopen("c://test//output.out", "r", stdin); freopen("c://test//output1.out", "w", stdout); } int main() { //fre(); while(~scanf("%s%lf%lf", s1, &th1, &lp1)) { int ans1 = solve(th1, lp1); scanf("%s%lf%lf", s2, &th2, &lp2); int ans2 = solve(th2, lp2); if(s1[0] == 'B'){ // 不變 } else if(s1[0] == 'D'){ if(ans1 == 1) ans1 = 3; else if(ans1 == 2) ans1 = 2; else if(ans1 == 3) ans1 = 4; else if(ans1 == 4) ans1 = 1; } else if(s1[0] == 'C'){ if(ans1 == 1) ans1 = 4; else if(ans1 == 2) ans1 = 2; else if(ans1 == 3) ans1 = 1; else if(ans1 == 4) ans1 = 3; } if(s2[0] == 'B'){ // 不變 } else if(s2[0] == 'D'){ if(ans2 == 1) ans2 = 3; else if(ans2 == 2) ans2 = 2; else if(ans2 == 3) ans2 = 4; else if(ans2 == 4) ans2 = 1; } else if(s2[0] == 'C'){ if(ans2 == 1) ans2 = 4; else if(ans2 == 2) ans2 = 2; else if(ans2 == 3) ans2 = 1; else if(ans2 == 4) ans2 = 3; } /* if(s1[0] == s2[0]){ } else if(s1[0] == 'B' && s2[0] == 'C' || s1[0] == 'C' && s2[0] == 'D' || s1[0] == 'D' && s2[0] == 'B'){ if(ans2 == 1) ans2 = 4; else if(ans2 == 2) ans2 = 2; else if(ans2 == 3) ans2 = 1; else if(ans2 == 4) ans2 = 3; } else{ if(ans2 == 1) ans2 = 3; else if(ans2 == 2) ans2 = 2; else if(ans2 == 3) ans2 = 4; else if(ans2 == 4) ans2 = 1; } */ if(ans1 == ans2) puts("YES"); else puts("NO"); } } /* BC 32 11 CD 59 11 */
H. Homework
對於最優解,將兩門課程合併然後貪心取截止日期最早的即可。
對於最劣解,建圖:
$S\rightarrow$每份數學作業連邊,容量$1$。
每份資訊作業$\rightarrow T$連邊,容量$1$。
每一天拆成兩個點,內部連邊,容量$1$。
每份作業與對應天連邊,容量$1$。
則每一條$S$到$T$的增廣路徑都對應某一天無論選哪種課程答案都會加一,而某一天只有一種課程的自然不會有流量。
故此時用題中所給貪心策略求出的答案就是這個圖的最大流。
#include<cstdio> #include<queue> #include<vector> #include<algorithm> using namespace std; const int N=1500; int n,m,lim,i,j,a[N],b[N];vector<int>g[N];priority_queue<int,vector<int>,greater<int> >q; int ans; namespace Flow{ const int inf=~0U>>2; struct E{int t,f;E*nxt,*pair;}*g[N],*d[N],pool[1000000],*cur=pool; int S,T,h[N],gap[N],maxflow; inline void add(int s,int t,int f){ E*p=cur++;p->t=t;p->f=f;p->nxt=g[s];g[s]=p; p=cur++;p->t=s;p->f=0;p->nxt=g[t];g[t]=p; g[s]->pair=g[t];g[t]->pair=g[s]; } int sap(int v,int flow){ if(v==T)return flow; int rec=0; for(E*p=d[v];p;p=p->nxt)if(h[v]==h[p->t]+1&&p->f){ int ret=sap(p->t,min(flow-rec,p->f)); p->f-=ret;p->pair->f+=ret;d[v]=p; if((rec+=ret)==flow)return flow; } if(!(--gap[h[v]]))h[S]=T; gap[++h[v]]++;d[v]=g[v]; return rec; } void init(int o){ S=o+1; T=S+1; } int work(){ for(gap[0]=T,i=1;i<=T;i++)d[i]=g[i]; while(h[S]<T)maxflow+=sap(S,inf); return maxflow; } } int main(){ scanf("%d%d",&n,&m); lim=400; //[1..m] [m+1..n] Flow::init(n+lim*2); for(i=1;i<=n;i++){ scanf("%d%d",&a[i],&b[i]); g[a[i]].push_back(b[i]); if(i<=m){ Flow::add(Flow::S,i,1); for(j=a[i];j<=b[i];j++)Flow::add(i,j+n,1); }else{ Flow::add(i,Flow::T,1); for(j=a[i];j<=b[i];j++)Flow::add(j+n+lim,i,1); } } for(i=1;i<=lim;i++)Flow::add(i+n,i+n+lim,1); for(i=1;i<=lim;i++){ for(j=0;j<g[i].size();j++)q.push(g[i][j]); while(!q.empty()){ int x=q.top(); if(x<i)q.pop();else break; } if(!q.empty())q.pop(),ans++; } printf("%d\n%d",ans,Flow::work()); }
I. Starting a Scenic Railroad Service
對於乘客自己選座的情況:答案為最大的和某個區間相交的區間總數加$1$。
對於系統自己選座的情況:答案為最大的單站所需乘客數。
字首和計算即可。
時間複雜度$O(n)$。
#include<cstdio> #include<algorithm> using namespace std; const int N=200010; int n,i,a[N],b[N],s[N],l[N],r[N],ans1,ans2; int main(){ scanf("%d",&n); for(i=1;i<=n;i++){ int x,y; scanf("%d%d",&x,&y); a[x]++; b[y]++; l[i]=x; r[i]=y; s[x]++; s[y]--; } for(i=1;i<N;i++)a[i]+=a[i-1],b[i]+=b[i-1]; for(i=1;i<=n;i++)ans1=max(ans1,a[r[i]-1]-b[l[i]]); for(i=1;i<N;i++)ans2=max(ans2,s[i]+=s[i-1]); printf("%d %d",ans1,ans2); }
J. String Puzzle
每個位置只會和前面一個位置直接通過資訊等價,將其看成有根樹的父親,則將所有已知字元和詢問都挪到根即可處理問題。
對於挪到根,只需要雙指標列舉所有資訊,然後減去一個等差數列,保證每次跳過一條資訊。
時間複雜度$O(m^2)$。
#include<cstdio> #include<map> using namespace std; const int N=1100; int n,a,b,q,i,x,m,known[N];char ch[N][9]; int y[N],h[N]; map<int,char>T; struct E{int l,d;E(){}E(int _l,int _d){l=_l,d=_d;}}e[N]; inline int go(int x){ int i=m; while(1){ while(i&&e[i].l>x)i--; int l=e[i].l,d=e[i].d; if(!i||!d)break; x-=(x-l)/d*d; while(x>=l)x-=d; } return x; } int main(){ scanf("%d%d%d%d",&n,&a,&b,&q); for(i=1;i<=a;i++)scanf("%d%s",&known[i],ch[i]); for(i=1;i<=b;i++)scanf("%d%d",&y[i],&h[i]); y[b+1]=n+1; for(i=1;i<=b;i++)e[++m]=E(y[i],h[i]?y[i]-h[i]:0); for(i=1;i<=a;i++)T[go(known[i])]=ch[i][0]; while(q--){ scanf("%d",&x); x=go(x); if(T.find(x)==T.end())putchar('?');else putchar(T[x]); } }
K. Counting Cycles
求出DFS生成樹,將非樹邊的端點作為關鍵點求出虛樹,則新圖中點數為$O(m-n)$,且答案不變。
那麼每個簡單環只可能是若干非樹邊以及對應樹上路徑異或後的結果,暴力列舉所有情況然後檢驗即可。
時間複雜度$O(n+m+2^{m-n}(m-n)^2)$。
#include<cstdio> const int N=110000,M=20; int n,m,i,j,x,y,g[N],v[N<<1],nxt[N<<1],ed,dfn,vis[N],f[N],d[N],pre[N],w[N]; int e[M][2],cnt,ans; int id[N],vip[N],cv; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void dfs(int x,int y){ int d=0; vis[x]=++dfn; for(int i=g[x];i;i=nxt[i]){ int u=v[i]; if(u==y)continue; if(!vis[u]){ f[u]=x; dfs(u,x); if(!id[u])continue; d++; id[x]^=id[u]; }else if(vis[u]<vis[x]){ vip[u]=vip[x]=1; e[cnt][0]=x; e[cnt][1]=u; cnt++; } } if(d>1)vip[x]=1; if(vip[x]){ id[x]=x; vip[x]=++cv; for(int i=g[x];i;i=nxt[i]){ int u=v[i]; if(f[u]!=x)continue; int t=id[u]; if(t)pre[vip[t]]=vip[x]; } } } int F(int x){return f[x]==x?x:f[x]=F(f[x]);} inline void merge(int x,int y){ d[x]++,d[y]++; if(F(x)!=F(y))f[f[x]]=f[y]; } inline void addcir(int x,int y){ merge(x,y); while(x!=y)w[x]^=1,x=pre[x]; } inline bool check(int S){ int i,j; for(i=1;i<=cv;i++)f[i]=i,d[i]=w[i]=0; for(i=0;i<cnt;i++)if(S>>i&1)addcir(e[i][0],e[i][1]); for(i=1;i<=cv;i++)if(pre[i]&&w[i])merge(i,pre[i]); for(i=1;i<=cv;i++)if(d[i]!=0&&d[i]!=2)return 0; for(i=1;i<=cv;i++)if(d[i])break; for(j=i+1;j<=cv;j++)if(d[j]&&F(i)!=F(j))return 0; return 1; } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=m;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x); for(i=1;i<=n;i++)if(!vis[i])dfs(i,0); for(i=0;i<cnt;i++)for(j=0;j<2;j++)e[i][j]=vip[e[i][j]]; for(i=1;i<1<<cnt;i++)if(check(i))ans++; printf("%d",ans); }