XV Open Cup named after E.V. Pankratiev. GP of America
A. Area of Effect
首先最优解中必有一个点在圆的边界上。
若半径就是$R$,则枚举一个点,然后把剩下的事件极角扫描即可,时间复杂度$O(m(n+m)\log(n+m))$。
否则圆必然撞到了两个圆,枚举一个点以及两个圆,二分出最大的半径,然后统计内部点数即可,时间复杂度$O(n^2m(n+m))$。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; typedef long long ll; typedef long double ld; typedef pair<ld,int>P; const int N=2100; const ld eps=1e-9,eps2=1e-4,pi=acos(-1.0); int n,m,i,j,k,ans=1;ll R,d[N][N];P q[N*2]; struct C{ll x,y,r;}a[N]; struct Circle{ ld x,y,r; Circle(){} Circle(ld _x,ld _y,ld _r){x=_x,y=_y,r=_r;} }; inline int sgn(ld x){ if(x>eps)return 1; if(x<-eps)return -1; return 0; } inline int sgn2(ld x){ if(x>eps2)return 1; if(x<-eps2)return -1; return 0; } inline ll sqr(ll x){return x*x;} inline ld sqr(ld x){return x*x;} inline void fix(ld&x){ while(sgn(x)<0)x+=pi*2; while(sgn(x-pi*2)>=0)x-=pi*2; x=max(x,(ld)0.0); } inline void solve1(int x){ int i,ret=1,cnt=0; for(i=1;i<=n+m;i++)if(i!=x){ if(d[x][i]>sqr(R*2+a[i].r))continue; if(d[x][i]==sqr(R*2+a[i].r)&&i<=n)continue; ld A=R,B=R+a[i].r,C=sqrt(d[x][i]); if(i<=n)B-=1e-6; ld o=acos((A*A+d[x][i]-B*B)/(2.0*A*C)); ld w=atan2(a[i].y-a[x].y,a[i].x-a[x].x); ld l=w-o,r=w+o; fix(l),fix(r); if(!sgn(l-r))r=l+eps; int v=i<=n?-N:1; if(l>r)ret+=v; q[++cnt]=P(l,v),q[++cnt]=P(r,-v); } ans=max(ans,ret); sort(q+1,q+cnt+1); for(i=1;i<=cnt;i++)ans=max(ans,ret+=q[i].second); } inline void circle_intersection(const Circle&a,const Circle&b,ld&X,ld&Y,int t){ ld d2=sqr(a.x-b.x)+sqr(a.y-b.y),d=sqrt(d2); ld l=(d2+sqr(a.r)-sqr(b.r))/(2.0*d),h=sqrt(max(sqr(a.r)-sqr(l),(ld)0.0)); ld vlx=(b.x-a.x)/d*l,vly=(b.y-a.y)/d*l; ld vhx=-(b.y-a.y)/d*h,vhy=(b.x-a.x)/d*h; if(t==1)vhx*=-1,vhy*=-1; X=a.x+vlx+vhx; Y=a.y+vly+vhy; } inline void work(ld x,ld y,ld r){ for(int i=1;i<=n;i++)if(sgn2(sqr(x-a[i].x)+sqr(y-a[i].y)-sqr(r+a[i].r))<0)return; int ret=0; r=sqr(r); for(int i=n+1;i<=n+m;i++)if(sgn2(sqr(x-a[i].x)+sqr(y-a[i].y)-r)<=0)ret++; ans=max(ans,ret); } inline void solve3(int x,int y,int z){ if(d[x][y]>=sqr(R*2+a[x].r+a[y].r))return; for(int _=0;_<2;_++){ ld l=(sqrt(d[x][z])-a[x].r)/2.0,r=R,X,Y; for(int i=0;i<100;i++){ ld mid=(l+r)/2.0; circle_intersection(Circle(a[x].x,a[x].y,a[x].r+mid),Circle(a[z].x,a[z].y,mid),X,Y,_); if(sgn2(sqr(X-a[y].x)+sqr(Y-a[y].y)-sqr(a[y].r+mid))>=0)l=mid;else r=mid; } circle_intersection(Circle(a[x].x,a[x].y,a[x].r+l),Circle(a[z].x,a[z].y,l),X,Y,_); work(X,Y,l); } } int main(){ scanf("%d%d%lld",&n,&m,&R); for(i=1;i<=n;i++)scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].r); for(i=1;i<=m;i++)scanf("%lld%lld",&a[i+n].x,&a[i+n].y); for(i=1;i<=n+m;i++)for(j=1;j<=n+m;j++)d[i][j]=sqr(a[i].x-a[j].x)+sqr(a[i].y-a[j].y); for(i=1;i<=m;i++)solve1(i+n); for(i=1;i<=n;i++)for(j=1;j<i;j++)for(k=1;k<=m;k++)solve3(i,j,k+n); printf("%d",ans); }
B. Canyon Mapping
二分答案,求出多边形的包围盒,那么某个正方形的某个顶点必然与包围盒的某个顶点重合,爆搜即可。
时间复杂度$O(4^3n\log w)$。
C. Magic Checkerboard
枚举奇偶性然后贪心构造即可。
#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 = 2020, M = 0, Z = 1e9 + 7; LL inf = 1e18; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n,m; int a[N][N]; int b[N][N]; void tryit(int y, int x, int oe) { int val = 2 - oe; if(x > 1)gmax(val, b[y][x - 1] + 1); if(y > 1)gmax(val, b[y - 1][x] + 1); if(val % 2 != oe)++val; b[y][x] = val; } void hahatry(int y, int x) { int val = 1; if(x > 1)gmax(val, b[y][x - 1] + 1); if(y > 1)gmax(val, b[y - 1][x] + 1); b[y][x] = val; } LL checkok() { LL sum = 0; for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) { if(i > 1 && b[i][j] <= b[i - 1][j])return inf; if(j > 1 && b[i][j] <= b[i][j - 1])return inf; if(i > 1) { if(j > 1) { if((b[i][j] ^ b[i - 1][j - 1]) % 2 == 0)return inf; } if(j < m) { if((b[i][j] ^ b[i - 1][j + 1]) % 2 == 0)return inf; } } sum += b[i][j]; } } return sum; } LL solve() { for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) { if(i > 1) { if(j > 1 && a[i][j] * a[i - 1][j - 1]) { if((a[i][j] ^ a[i - 1][j - 1]) % 2 == 0)return -1; } if(j < m && a[i][j] * a[i - 1][j + 1]) { if((a[i][j] ^ a[i - 1][j + 1]) % 2 == 0)return -1; } } } } LL ans = inf; for(int x = 0; x < 2; ++x) //int x = 1; { for(int y = 0; y < 2; ++y) //int y = 0; { memcpy(b, a, sizeof(a)); for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j)if(b[i][j] == 0) { if((i + j) % 2 == 0) { if((i - 1) % 2 == 0) { if(n == 1 || m == 1)hahatry(i,j); else tryit(i, j, x); } else { if(n == 1 || m == 1)hahatry(i,j); else tryit(i, j, 1 ^ x); } } else { if((i - 1) % 2 == 0) { if(n == 1 || m == 1)hahatry(i,j); else tryit(i, j, y); } else { if(n == 1 || m == 1)hahatry(i,j); else tryit(i, j, 1 ^ y); } } } } if(0)for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j)printf("%d ", b[i][j]); puts(""); } gmin(ans, checkok()); } } if(ans == inf)ans = -1; return ans; } int main() { while(~scanf("%d%d",&n,&m)) { for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) { scanf("%d", &a[i][j]); } } printf("%lld\n", solve()); } return 0; } /* 【trick&&吐槽】 4 4 1 2 3 0 0 0 5 6 0 0 7 8 7 0 0 10 4 4 1 2 3 0 0 0 5 6 0 4 7 8 7 0 0 10 2 3 0 0 0 0 0 0 【题意】 【分析】 【时间复杂度&&优化】 */
D. Extensive OR
从高到低考虑每一位,一旦有个数字脱离了限制,也就是说这个数字这一位是$1$,但是强制填了$0$。那么除了这一位之外,其它位可以乱填,它自己方案唯一。
枚举脱离的那一位,设$f[i][j][l]$表示前$i$个数字,当前位$xor$为$j$,是否有数字脱离限制的方案数。如此求出$g[i]$表示$i$个数(允许重复)的答案。
考虑容斥,枚举$n$的所有等价类方案,贡献为:
$(-1)^{n-等价类个数}\ \ \ \ \ \ \ \times(每个等价类大小-1)!之积\times 方案数$
$方案数=g[奇数大小的等价类个数]\times g[2]^{偶数大小的等价类个数}$。
最后答案再除以$n!$即可。
时间复杂度$O(nk|S|+Bell(n))$。
#include<cstdio> #include<cstring> const int N=10,P=1000000007,inv=(P+1)/2; int n,m,K,i,j,k,x,y,o,f[N][2][2],g[N],w[N],ans; char a[5000050],flag[N]; int pow(int a,int b){ int t=1; for(;b;b>>=1,a=1LL*a*a%P)if(b&1)t=1LL*t*a%P; return t; } inline void up(int&a,int b){a=a+b<P?a+b:a+b-P;} void dfs(int x,int y){ if(x==n){ int i,j,k=(n-y)&1?-1:1,t=0; for(i=1;i<=y;i++){ for(j=2;j<w[i];j++)k*=j; if(w[i]&1)t++; } if(k<0)k+=P; ans=(1LL*k*g[t]%P*pow(g[2],y-t)+ans)%P; return; } for(int i=1;i<=y+1;i++){ w[i]++; dfs(x+1,i>y?i:y); w[i]--; } } int main(){ scanf("%d%d%s",&n,&K,a); m=strlen(a); for(i=0;i<m;i++)for(j=1,k=i+m;j<K;j++,k+=m)a[k]=a[i]; m*=K; for(i=0;i<m;i++)a[i]-='0'; for(i=m-1;~i;i--){ a[i]^=1; if(!a[i])break; } f[0][0][0]=1; y=pow(2,m); for(i=0;i<m;i++)x=(x*2+a[i])%P; up(x,1); for(i=m-1;~i;i--){ y=1LL*y*inv%P; o=a[m-i-1]; if(o)up(x,P-y); if(o){ for(j=1;j<=n;j++){ f[j][0][0]=f[j][0][1]=f[j][1][0]=f[j][1][1]=0; for(k=0;k<2;k++){ if(f[j-1][k][0]){ f[j][k^1][0]=(1LL*f[j-1][k][0]*x+f[j][k^1][0])%P; up(f[j][k][1],f[j-1][k][0]); } if(f[j-1][k][1]){ f[j][k][1]=(1LL*f[j-1][k][1]*y+f[j][k][1])%P; f[j][k^1][1]=(1LL*f[j-1][k][1]*x+f[j][k^1][1])%P; } } } }else{ for(j=1;j<=n;j++){ f[j][0][0]=f[j][0][1]=f[j][1][0]=f[j][1][1]=0; for(k=0;k<2;k++){ if(f[j-1][k][0]){ f[j][k][0]=(1LL*f[j-1][k][0]*x+f[j][k][0])%P; } if(f[j-1][k][1]){ f[j][k][1]=(1LL*f[j-1][k][1]*x+f[j][k][1])%P; } } } } for(j=1;j<=n;j++)if(!flag[j]){ up(g[j],f[j][0][1]); if(o&(j&1))flag[j]=1; } } for(i=1;i<=n;i++)if(!flag[i])up(g[i],1); g[0]=g[1]=1; dfs(0,0); for(i=2;i<=n;i++)ans=1LL*ans*pow(i,P-2)%P; printf("%d",ans); }
E. Primal Partitions
设$f[i][j]$表示把$[1,j]$分成$i$段的最大得分,注意到区间$\gcd$只有本质不同的$O(n\log v)$段,用ST表优化转移即可。
时间复杂度$O(nk\log n)$。
#include<cstdio> const int N=20010,M=1000010; int n,m,i,j,tot,p[M],vis[M],w[M],l[N],v[N]; int a[N],Log[N],dp[N],f[15][N]; struct E{ int x,y,r,w; E(){} E(int _x,int _y,int _r,int _w){x=_x,y=_y,r=_r,w=_w;} }e[M]; inline int max(int a,int b){return a>b?a:b;} inline int min(int a,int b){return a<b?a:b;} inline void up(int&a,int b){a<b?(a=b):0;} int gcd(int a,int b){return b?gcd(b,a%b):a;} inline int ask(int l,int r){ int k=Log[r-l+1]; return max(f[k][l],f[k][r-(1<<k)+1]); } int main(){ scanf("%d%d",&n,&m); for(i=2;i<=1000000;i++){ if(!vis[i])vis[i]=i,p[tot++]=i; for(j=0;j<tot&&i*p[j]<=1000000;j++){ vis[i*p[j]]=p[j]; if(i%p[j]==0)break; } } for(i=2;i<=1000000;i++){ j=i; while(j>1){ w[i]=max(w[i],vis[j]); j/=vis[j]; } } for(i=1;i<=n;i++)scanf("%d",&a[i]); tot=0; for(i=1;i<=n;i++)for(v[i]=a[i],j=l[i]=i;j;j=l[j]-1){ v[j]=gcd(v[j],a[i]); while(l[j]>1&&gcd(a[i],v[l[j]-1])==gcd(a[i],v[j]))l[j]=l[l[j]-1]; e[++tot]=E(l[j],j,i,w[v[j]]); } dp[1]=a[1]; for(i=2;i<=n;i++)dp[i]=gcd(dp[i-1],a[i]); for(i=1;i<=n;i++)dp[i]=w[dp[i]]; for(i=2;i<=n+1;i++)Log[i]=Log[i>>1]+1; while(m>1){ m--; for(i=0;i<=n;i++)f[0][i]=dp[i],dp[i]=0; for(j=1;j<15;j++)for(i=0;i+(1<<j)-1<=n;i++)f[j][i]=max(f[j-1][i],f[j-1][i+(1<<(j-1))]); for(i=1;i<=tot;i++)up(dp[e[i].r],min(ask(e[i].x-1,e[i].y-1),e[i].w)); } printf("%d",dp[n]); }
F. Sand Art
二分差值,显然最高的是不变的,对于剩下的用网络流判定是否有解即可。
#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 = 420, M = 1e6 + 10, Z = 1e9 + 7; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; const double inf = 1e9; int ST, ED; int first[N], id; int w[M], nxt[M]; double cap[M]; void ins(int x, int y, double cap_) { w[++ id] = y; cap[id] = cap_; nxt[id] = first[x]; first[x] = id; w[++ id] = x; cap[id] = 0; nxt[id] = first[y]; first[y] = id; } int d[N]; const double eps = 1e-6; int sgn(double x) { if(fabs(x) < eps) return 0; return x > 0 ? 1 : -1; } bool bfs() { MS(d, -1); queue<int> q; q.push(ST); d[ST] = 0; while(! q.empty()){ int x = q.front(); q.pop(); for(int z = first[x]; z; z = nxt[z]) if(sgn(cap[z])){ int y = w[z]; if(d[y] == -1){ d[y] = d[x] + 1; q.push(y); if(y == ED) return 1; } } } return 0; } double dfs(int x, double all) { if(x == ED) return all; double use = 0; for(int z = first[x]; z; z = nxt[z]) if(sgn(cap[z])){ int y = w[z]; if(d[y] == d[x] + 1){ double tmp = dfs(y, min(cap[z], all - use)); cap[z] -= tmp; cap[z ^ 1] += tmp; use += tmp; if(sgn(use - all) == 0) break; } } if(sgn(use) == 0) d[x] = -1; return use; } double dinic() { double ret = 0; while(bfs()) ret += dfs(ST, inf); return ret; } int n, m; double v[N], h[N], W, H, x[N]; double minv[N][N], maxv[N][N]; double minh, maxh; bool build(double mid) { double top = maxh - mid; MS(first, 0); id = 1; double allv = 0; for(int i = 1; i <= n; i ++){ if(sgn(top - h[i]) > 0) allv += (top - h[i]) * x[i]; } for(int j = 1; j <= m; j ++){ for(int i = 1; i <= n; i ++){ if(sgn(top - h[i]) > 0) { double cap_ = min((top - h[i]) * x[i], maxv[i][j] - minv[i][j]); ins(j, i + m, cap_); } } } ST = 0, ED = n + m + 1; for(int i = 1; i <= n; i ++){ if(sgn(top - h[i]) > 0) ins(i + m, ED, (top - h[i]) * x[i]); } for(int i = 1; i <= m; i ++){ ins(ST, i, v[i]); } if(sgn(dinic() - allv) != 0) return 1; return 0; } int main() { scanf("%d%d%lf%lf", &n, &m, &W, &H); for(int i = 1; i <= m; i ++){ scanf("%lf", &v[i]); } for(int i = 1; i < n; i ++){ scanf("%lf", &x[i]); } x[n] = W; for(int i = n; i >= 1; i --){ x[i] = x[i] - x[i - 1]; //x[i] *= H; } // 每个容器的宽度 for(int i = 1; i <= n; i ++){ for(int j = 1; j <= m; j ++){ scanf("%lf", &minv[i][j]); } } for(int i = 1; i <= n; i ++){ for(int j = 1; j <= m; j ++){ scanf("%lf", &maxv[i][j]); } } maxh = 0, minh = 1e9; for(int i = 1; i <= n; i ++){ for(int j = 1; j <= m; j ++){ h[i] += minv[i][j] / x[i]; v[j] -= minv[i][j]; } gmax(maxh, h[i]); gmin(minh, h[i]); } double r = maxh - minh, l = 0; for(int tim = 1; tim <= 200; tim ++){ double mid = (l + r) / 2; if(build(mid)) l = mid; else r = mid; } printf("%.6f\n", l); return 0; } /* 【trick&&吐槽】 2 2 5 5 2.0 2.0 4.0 1.0 0.0 0.0 1.0 1.0 0.0 1.0 2.0 2 2 5 5 2.0 2.0 4.0 1.0 0.0 0.0 1.0 1.5 0.0 0.0 2.0 2 5 11 10 3.0 4.0 4.0 9.0 2.0 4.0 2.0 2.0 1.0 0.5 0.25 0.0 2.0 0.0 4.0 1.0 2.0 2.0 3.0 4.0 0.75 0.0 2.1 0.0 5.1 1.1 【题意】 【分析】 【时间复杂度&&优化】 */
G. String Stretching
枚举长度$len$,那么$len$必须要是$n$的约数,然后枚举每个长度为$len$的子串$S$,检查是否可行。
对于检查,设$f[i][j]$表示区间$[i,j]$能否通过$S$产生,要么在末尾产生某个长度为$len$倍数的区间,要么接着匹配一位,时间复杂度$O(\frac{n^3}{len})$。
总时间复杂度$O(n^3d(n)\log n)$。
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; const int N=210; char a[N],b[N],c[N],fin[N]; int n,i; int cnt; bool f[N][N]; string best; inline bool check(int len,string cur){ int i,j,k; //cout<<len<<" "<<cur<<endl; for(i=1;i<=len;i++)b[i]=cur[i-1]; int tot=0; for(i=1;i*len<=n;i++)for(j=1;j<=len;j++)c[++tot]=b[j]; sort(c+1,c+n+1); for(i=1;i<=n;i++)if(c[i]!=fin[i])return 0; for(i=0;i<=n+1;i++)for(j=0;j<=n+1;j++)f[i][j]=0; for(i=n;i;i--){ f[i][i-1]=1; for(j=i;j<=n;j++){ for(k=j-len;k>=i-1;k-=len){ if(f[i][k]&&f[k+1][j]){f[i][j]=1;break;} } int o=j-i+1; o%=len; if(!o)o+=len; if(f[i][j-1]&&a[j]==b[o])f[i][j]=1; } } return f[1][n]; } void solve(int len){ if(n%len)return; for(int i=1;i+len-1<=n;i++){ string now=""; for(int j=0;j<len;j++)now.push_back(a[i+j]); if(cnt&&best<=now)continue; if(check(len,now)){ cnt++; best=now; } } if(cnt){ cout<<best<<endl; exit(0); } } int main(){ scanf("%s",a+1); n=strlen(a+1); for(i=1;i<=n;i++)fin[i]=a[i]; sort(fin+1,fin+n+1); for(i=1;i<n;i++)solve(i); puts(a+1); } /* hhehellolloelhellolo */
H. Vending Machine
将差值看成边权,并添加$0$边来消除负数,首先可以贪心将每个点都买到剩一个。
那么若是树或者自环,则答案为每个点的入边中权值的最大值。
对于环来说,必须要删掉环上一条边,枚举每条边删除即可。
时间复杂度$O(n\log n)$。
#include<cstdio> #include<set> #include<algorithm> using namespace std; typedef long long ll; typedef pair<int,int>P;//w[son],son const int N=100010; int n,i,j,k,f[N],p[N],m[N],s[N],w[N]; ll ans; int t,q[N]; int fa[N],vf[N],vis[N]; set<P>T[N]; int F(int x){return fa[x]==x?x:fa[x]=F(fa[x]);} inline void merge(int x,int y){ if(F(x)!=F(y))fa[fa[x]]=fa[y]; } inline ll cal(int x){ int y=f[x]; ll pre=T[y].rbegin()->first; T[y].erase(P(w[x],x)); ll now=T[y].rbegin()->first; T[y].insert(P(w[x],x)); return now-pre; } int main(){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d%d%d%d",&f[i],&p[i],&m[i],&s[i]); for(i=1;i<=n;i++)T[i].insert(P(0,0)); for(i=1;i<=n;i++){ w[i]=m[f[i]]-p[i]; T[f[i]].insert(P(w[i],i)); } for(i=1;i<=n;i++)ans+=1LL*(T[i].rbegin()->first)*s[i]; for(i=1;i<=n;i++)fa[i]=i; for(i=1;i<=n;i++)merge(i,f[i]); for(i=1;i<=n;i++)if(!vf[F(i)]){ vf[fa[i]]=1; for(j=i;!vis[j];j=f[j])vis[j]=1; q[t=1]=j; for(k=f[j];k!=j;k=f[k])q[++t]=k; if(t==1)continue; ll tmp=-(1LL<<60); for(j=1;j<=t;j++){ tmp=max(tmp,cal(q[j])); } ans+=tmp; } printf("%lld",ans); } /* 5 5 9 2 2 1 1 7 4 2 3 6 3 2 2 9 6 1 4 5 1 */
I. Rainbow Zamboni
首先通过公式直接求出终点,然后倒着模拟,最多模拟$O(R+C)$轮后每个点都会被经过至少一次,无需接着模拟。
时间复杂度$O((R+C)^2)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; ll x, y; ll n; int R,C; int ret; const int N=2222; char f[N][N],vis[N][N]; void solve(int d, ll i) { if(d==0){ x+=i; } if(d==1){ y-=i; } if(d==2){ x-=i; } if(d==3){ y+=i; } if(d==0){ y++; } if(d==1){ x++; } if(d==2){ y--; } if(d==3){ x--; } } ll mo(ll a,ll b){return (a%b+b)%b;} inline void col(int x,int y,int c){ if(!vis[x][y]){ vis[x][y]=1; f[x][y]=c; ret--; } } inline void moveup(int x,int l,int r,int c){ for(int i=l;i<=r;i++)col(x,i,c); } inline void moveright(int y,int l,int r,int c){ for(int i=l;i<=r;i++)col(i,y,c); } inline void walk(ll x1,ll y1,ll x2,ll y2,int c){ if(x1==x2){ if(y1>y2)swap(y1,y2); ll k=y2-y1+1; y1=mo(y1,C); if(k>=C)moveup(mo(x1,R),0,C-1,c); else{ if(y1+k-1<C)moveup(mo(x1,R),y1,y1+k-1,c); else{ moveup(mo(x1,R),y1,C-1,c); moveup(mo(x1,R),0,mo(y2,C),c); } } }else{ if(x1>x2)swap(x1,x2); ll k=x2-x1+1; x1=mo(x1,R); if(k>=R)moveright(mo(y1,C),0,R-1,c); else{ if(x1+k-1<R)moveright(mo(y1,C),x1,x1+k-1,c); else{ moveright(mo(y1,C),x1,R-1,c); moveright(mo(y1,C),0,mo(x2,R),c); } } } } void solve2(ll n, int d,int c) { ll xx = x, yy = y; if(d == 0){ // rgt x += n; } else if(d == 1){ // down y -= n; } else if(d == 2){ // lft x -= n; } else if(d == 3){ // up y += n; } walk(xx,yy,x,y,c); if(d == 0){ // rgt y ++; } else if(d == 1){ // down x ++; } else if(d == 2){ // lft y --; } else if(d == 3){ // up x --; } } int main() { scanf("%d%d",&R,&C); scanf("%lld%lld",&x,&y); x=R-x+1; swap(x,y); swap(R,C); scanf("%lld", &n); x--,y--; x%=R; y%=C; ll A = n / 4, B = n % 4; x += -2 * A + 1, y += -2 * A; x%=R; y%=C; for(int i = 0; i < B; i ++){ solve((i+3)%4, A * 4 + i); x%=R; y%=C; } for(int i=0;i<R;i++)for(int j=0;j<C;j++){ f[i][j]='.'; ret++; } if(n%4==0){ col(mo(x-1,R),mo(y,C),'@'); } if(n%4==1){ col(mo(x,R),mo(y+1,C),'@'); } if(n%4==2){ col(mo(x+1,R),mo(y,C),'@'); } if(n%4==3){ col(mo(x,R),mo(y-1,C),'@'); } for(ll i=n;i;i--){ solve2(i-1, i % 4,(i-1+26)%26+'A'); x%=R; y%=C; if(!ret)break; } for(int j=C-1;~j;j--){ for(int i=0;i<R;i++)putchar(f[i][j]); puts(""); } }
J. Zig Zag Nametag
贪心构造即可。
#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 = 1e6 + 30, 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, K; int f[N][26]; void print(int len, int first) { printf("%c", 'a' + first); for(int nxt = 0; nxt < 26; ++nxt) { int add = abs(first - nxt); if(len >= add && f[len - add][nxt] + 1 == f[len][first]) { print(len - add, nxt); break; } } } char s[N]; int main() { while(~scanf("%d",&K)) { int n = K / 25 + (K % 25 > 0) + 1; for(int i = 1; i <= n; ++i) { s[i] = i % 2 == 1 ? 'a' : 'z'; } s[n + 1] = 0; int dif = (n - 1) * 25 - K; if(n == 2) { s[2] -= dif; } else { int diff = dif / 2; s[2] -= diff; if(s[n] == 'a')s[n] += dif % 2; else s[n] -= dif % 2; } puts(s + 1); } return 0; for(K = 1; K <= 200; ++K) { MS(f, 63); for(int j = 0; j < 26; ++j) { f[0][j] = 1; } for(int i = 0; i <= K; ++i) { for(int j = 0; j < 26; ++j)if(f[i][j] != inf) { for(int k = 0; k < 26; ++k) { int add = abs(j - k); gmin(f[i + add][k], f[i][j] + 1); } } } int len = -1, pos = 0; for(int i = 0; i < 26; ++i) { if(f[K][i] < len) { len = f[K][i]; pos = i; } } print(K, pos); puts(""); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */