A.Cram Time
显然$a,b$都是被用完的,我们也只会用一个前缀。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 100005 int n,m; vector <int> p,q; inline bool check(int x) {return 1ll*x*(1+x)/2<=n+m;} int main () { //freopen("a.in","r",stdin); n=read(),m=read(); int l=1,r=1e5,mid,ans=0; while (l<=r) { mid=l+r>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } FOR2(ans,1,i) if(n>=i) n-=i,q.push_back(i); else m-=i,p.push_back(i); int size=q.size(); printf("%d\n",size); for1(1,size,i) printf("%d ",q[i-1]); puts(""); size=p.size(); printf("%d\n",size); for1(1,size,i) printf("%d ",p[i-1]); puts(""); }
B.Minimum path
我们一定是把某条路径的前几个不是$a$的变成$a$。
dp出来最长的$a$的前缀长度,然后把所有满足条件的点都保存下来。
之后逐位贪心,因为$dis$不断增大,所以是$O(n^2)$的。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 2005 int n,m; char a[M][M]; int f[M][M],size; bool vis[M][M]; struct node {int x,y;}q[M*M],p[M*M]; inline bool check(int c) { c+='a'-1; for1(1,size,i) { int x=q[i].x; int y=q[i].y; if(a[x+1][y]==c||a[x][y+1]==c) return 1; } return 0; } inline void move_(int c) { c+='a'-1; int cnt=0; for1(1,size,i) { int x=q[i].x; int y=q[i].y; if(a[x+1][y]==c&&!vis[x+1][y]) vis[x+1][y]=1,p[++cnt]=(node){x+1,y}; if(a[x][y+1]==c&&!vis[x][y+1]) vis[x][y+1]=1,p[++cnt]=(node){x,y+1}; } size=cnt; for1(1,size,i) q[i]=p[i]; } int main () { //freopen("a.in","r",stdin); n=read(),m=read(); for1(1,n,i) scanf("%s",a[i]+1); if(m>=2*n-1) { for1(1,2*n-1,i) putchar('a'); puts(""); return 0; } memset(f,0x3f,sizeof(f)); f[1][1]=a[1][1]!='a'; for1(1,n,i) for1(1,n,j) { int c=a[i][j]!='a'; if(i-1) f[i][j]=min(f[i][j],f[i-1][j]+c); if(j-1) f[i][j]=min(f[i][j],f[i][j-1]+c); } int da=0; for1(1,n,i) for1(1,n,j) if(f[i][j]<=m) da=max(da,i+j-1); for1(1,n,i) for1(1,n,j) if(i+j-1==da&&f[i][j]<=m) q[++size]=(node){i,j}; for1(1,da,i) putchar('a'); if(f[1][1]>m) q[++size]=(node){1,1},da=1,putchar(a[1][1]); for1(da+1,2*n-1,i) { int x=1; while (!check(x)) ++x; move_(x); putchar('a'+x-1); } puts(""); }
C.Triple Flips
我觉得我学习了一种很好的思路。
当看到题解的一刻我觉得这个题很不好。
但是当我仔细分析之后我觉得这是一种非常正确且有效的思路。
首先我们对于$n$很小的点可以暴力状压,发现$n\leq 7$才可能无解。
接下来我们就考虑题目中给出的上界$\left \lfloor \frac{n}{3} \right \rfloor+12$。
我们可以理解为每一步操作至少使得端点为$1$的序列长度减$3$,之后当长度变小时就可以用状压了。
然后就很简单了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 200005 int n; bool vis[M]; int a[M],tot,e_size,head[M],dis[M]; set <pair<int,int> > q; struct node {int v,nxt;}e[M*2]; struct point {int x,y,z;}cc[M]; inline void e_add(int u,int v) { e[++e_size]=(node){v,head[u]}; head[u]=e_size; } inline void add_(int x,int y) { a[x]^=1,a[y]^=1,a[2*y-x]^=1; cc[++tot]=(point){x,y,2*y-x}; } inline void solve() { int m=min(n,12); int all=(1<<m)-1; for1(0,all,i) { for1(1,m,j) { for1(j+1,m,k) if(2*k-j<=m) e_add(i,i^(1<<j-1)^(1<<k-1)^(1<<2*k-j-1)); } } memset(dis,0x3f,sizeof(dis)); dis[0]=0; q.insert(make_pair(0,0)); while (!q.empty()) { auto t=*q.begin(); q.erase(q.begin()); if(vis[t.second]) continue; vis[t.second]=1; for(int i=head[t.second];i;i=e[i].nxt) { int v=e[i].v; if(dis[v]>dis[t.second]+1) { dis[v]=dis[t.second]+1; q.insert(make_pair(dis[v],v)); } } } } inline void trans_(int pre,int Max,int x) { if(!x) return; for(int i=head[x];i;i=e[i].nxt) { int v=e[i].v; if(dis[x]==dis[v]+1) { int cnt=0; int buc[3]={0}; FOR2(Max,1,j) if((x^v)>>j-1&1) buc[cnt++]=Max-j+1+pre; add_(buc[0],buc[1]); trans_(pre,Max,v); return; } } } int main () { // freopen("a.in","r",stdin); n=read(); for1(1,n,i) a[i]=read(); solve(); if(n<=12) { int zt=0; for1(1,n,i) zt=zt*2+a[i]; if(dis[zt]>n) puts("NO"); else { puts("YES"); trans_(0,n,zt); printf("%d\n",tot); for1(1,tot,i) printf("%d %d %d\n",cc[i].x,cc[i].y,cc[i].z); } return 0; } puts("YES"); for(int i=1;i<=n-12;) { while (!a[i]&&i!=n) ++i; if(i>n-12) break; if(!a[i+1]) { if(a[i+2]) add_(i,i+2); else add_(i,i+3); } else { if(a[i+2]) add_(i,i+1); else { int cnt=0; int buc[3]={0}; for1(i+3,i+5,j) if(a[j]) buc[cnt++]=j; if(!cnt) { add_(i,i+6),add_(i+1,i+7); } else if(cnt==1) { add_(i,buc[0]),add_(i+1,i+6); } else if(cnt==2) { add_(i,buc[0]),add_(i+1,buc[1]); } else { add_(i+1,i+3),add_(i,i+4); } } } } int zt=0; for1(1,12,i) a[i]=a[n-12+i]; for1(1,12,i) zt=zt*2+a[i]; trans_(n-12,12,zt); printf("%d\n",tot); for1(1,tot,i) printf("%d %d %d\n",cc[i].x,cc[i].y,cc[i].z); }
D.Familiar Operations
令$x=\prod pi^{ai}$。
我们可以$dfs$出所有把$ai$排序之后的序列,即本质不同的序列。
你会发现操作数并不会很多,所以并不用$dfs$出太多。
我之后直接用了$floyd$来搞$dis$,然后枚举到多少个因子就行了。
感觉自己好蠢,我总是想不到边权相同时直接$Bfs$是线性的。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> #include <unordered_map> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; typedef unsigned long long unll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 3005 #define mod 76543 #define N 1000005 bool vis[N]; int sta[M],zt[N]; int A[M],dis[M][M]; int cnt,a[M][10],f[M],s[N],g[N][10]; int x_[10]={0,2,3,5,7,11,13,17,19}; unordered_map <unll,int> st; inline void max(int &x,int y) {if(x<y) x=y;} inline void min(int &x,int y) {if(x>y) x=y;} inline void dfs(int x,int d,ll sum) { if(x==9) { f[++cnt]=d; for1(0,8,i) a[cnt][i]=sta[i]; unll t=0; for1(1,8,i) if(sta[i]) t=t*mod+sta[i]; st[t]=cnt; return; } for1(0,sta[x-1],i) { sta[x]=i; dfs(x+1,d*(i+1),sum); sum*=x_[x]; if(sum>200000000) break; } } inline int get_(int x) { int now=x; int b[10]={0}; for1(1,s[x],i) { int y=g[x][i]; while (!(now%y)) now/=y,++b[i]; } sort(b+1,b+s[x]+1); unll t=0; FOR2(s[x],1,i) t=t*mod+b[i]; return st[t]; } int main () { // freopen("a.in","r",stdin); sta[0]=30; dfs(1,1,1); memset(dis,0x3f,sizeof(dis)); for1(1,cnt,i) dis[i][i]=0; for1(1,cnt,i) { for1(1,8,j) { if(a[i][j]+1>a[i][j-1]) continue; unll x=0; ++a[i][j]; for1(1,8,k) if(a[i][k]) x=x*mod+a[i][k]; --a[i][j]; if(st.count(x)) { int t=st[x]; dis[i][t]=dis[t][i]=1; } } } for1(1,cnt,i) for1(1,cnt,j) if(dis[j][i]<100) for1(1,cnt,k) if(dis[i][k]<100) min(dis[j][k],dis[j][i]+dis[i][k]); for1(1,cnt,i) { for1(1,cnt,j) A[j]=dis[i][j],dis[i][j]=1e9; for1(1,cnt,j) min(dis[i][f[j]],A[j]); } for1(2,N-5,i) { if(s[i]) continue; int x=i; while (x+4<N) g[x][++s[x]]=i,x+=i; } int size=0; for1(1,cnt,i) if(!vis[f[i]]) vis[f[i]]=1,zt[++size]=f[i]; int Test_=read(); while (Test_--) { int x=read(),y=read(); x=get_(x),y=get_(y); int ans=1e9; for1(1,size,i) min(ans,dis[x][zt[i]]+dis[y][zt[i]]); printf("%d\n",ans); } }
E.Rain Protection
思路感觉还是很简单了,就是自己太蠢了,一开始思路错了结果打了好几个版本那个思路。
我们只保存可到达的$(x,0)$的区间。
令$x$为到$i$时的位置,$y$为$i-1$的某个位置,$g(x,i)=x+\frac{h*(p[i].x-x)}{p[i].y}$即对面线上的横坐标。
那么我们需要满足的条件是:
\begin{cases}
& \text l\leq y \leq r \\
& \text y-v*t\leq x \leq y+v*t \\
& \text g(y,i)-v*t\leq g(x,i-1)\leq g(y,i)+v*t
\end{cases}
并且式子可以化成$k1*y+b1\leq x \leq k2*y+b2$的形式。
这样解出来就是之后$x$的范围了。
观察式子其实就是以$y$为横坐标会出来一个平行四边形(或者两条平行线)。
直接找到满足横坐标范围内的纵坐标的$min,max$就行了。
好迷啊,必须要加$eps$才能过。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 200005 #define eps 1e-10 int n,w,h,e1,e2; struct node { double l,r; }; struct point { int t; double x,y; inline void in() { t=read(),x=read(),y=read(); } }p[M]; inline bool check(double v) { node a,t,tmp; a.l=a.r=e1; for(int i=1,las=0;i<=n;las=p[i].t,++i) { las=p[i].t-las; double k[4]={0,1-h/p[i-1].y,1-h/p[i].y,1-h/p[i-1].y}; double b[4]={0,h*p[i-1].x/p[i-1].y-las*v,h*p[i].x/p[i].y,h*p[i-1].x/p[i-1].y+las*v}; b[3]-=b[2],k[3]/=k[2],b[3]/=k[2]; b[1]-=b[2],k[1]/=k[2],b[1]/=k[2]; tmp.l=1e9,tmp.r=-1e9; t.l=max(a.l-las*v,k[3]*a.l+b[3]); t.r=min(a.l+las*v,k[1]*a.l+b[1]); if(t.l<t.r+eps) tmp.l=min(tmp.l,t.l),tmp.r=max(tmp.r,t.r); t.l=max(a.r-las*v,k[3]*a.r+b[3]); t.r=min(a.r+las*v,k[1]*a.r+b[1]); if(t.l<t.r+eps) tmp.l=min(tmp.l,t.l),tmp.r=max(tmp.r,t.r); if(k[3]!=1) { double x=(b[3]+las*v)/(1-k[3]); if(x>a.l&&x<a.r) { x-=las*v; tmp.l=min(tmp.l,x),tmp.r=max(tmp.r,x); } x=(las*v-b[3])/(k[3]-1); if(x>a.l&&x<a.r) { x+=las*v; tmp.l=min(tmp.l,x),tmp.r=max(tmp.r,x); } } if(k[1]!=1) { double x=(las*v-b[1])/(k[1]-1); if(x+eps>a.l&&x<a.r+eps) { x+=las*v; tmp.l=min(tmp.l,x),tmp.r=max(tmp.r,x); } x=(las*v+b[1])/(1-k[1]); if(x+eps>a.l&&x<a.r+eps) { x-=las*v; tmp.l=min(tmp.l,x),tmp.r=max(tmp.r,x); } } a=tmp; tmp.r=h*p[i].x/(h-p[i].y); tmp.l=(w*p[i].y-p[i].x*h)/(p[i].y-h); a.l=max(a.l,tmp.l); a.r=min(a.r,tmp.r); a.l=max(a.l,0),a.r=min(a.r,w); if(a.l>a.r+eps) return 0; } return 1; } int main () { //freopen("a.in","r",stdin); n=read(),w=read(),h=read(),e1=read(),e2=read(); p[0].x=(e1+e2)/2.0,p[0].y=h/2.0; for1(1,n,i) p[i].in(); double l=0,r=1e3+5,mid; for1(1,100,i) { mid=(l+r)/2; if(check(mid)) r=mid; else l=mid; } if(r>1000) puts("-1"); else printf("%.10f\n",r); }