XV Open Cup named after E.V. Pankratiev. GP of America

时间:2022-05-26 00:54:36

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&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */