A. Ducks in a Row
当$n imes k>|S|$时,显然无解。
否则最优解中翻转的区间一定两两不相交,设$f[i][j][x][y]$表示考虑前$i$个位置,第$i$个位置翻转情况为$j$,当前连续段长度为$x$,已有$y$个长度至少为$n$的$D$连续段的最少翻转次数,然后转移即可。
时间复杂度$O(|S|^2)$。
#include<cstdio> #include<cstring> const int N=2010,inf=10000000; int n,K,m,o,i,j,k,x,y,f[2][2][N][N],ans=inf; char a[N]; inline void up(int&a,int b){a>b?(a=b):0;} int main(){ scanf("%d%d%s",&n,&K,a+1); m=strlen(a+1); if(n*K>m)return puts("-1"),0; //D=1 //G=0 for(i=1;i<=m;i++)a[i]=a[i]=='D'; for(j=0;j<2;j++)for(x=0;x<=n;x++)for(y=0;y<=K;y++)f[0][j][x][y]=inf; f[0][0][0][0]=0; for(i=0;i<m;i++,o^=1){ for(j=0;j<2;j++)for(x=0;x<=n;x++)for(y=0;y<=K;y++)f[o^1][j][x][y]=inf; for(j=0;j<2;j++)for(x=0;x<=n;x++)for(y=0;y<=K;y++)if(f[o][j][x][y]<inf){ int w=f[o][j][x][y]; int pre=a[i]^j; for(k=0;k<2;k++){ int now=a[i+1]^k; int len=x,cnt=y; if(pre==now){ if(now)len++; }else{ if(pre&&x>=n)cnt++; len=now; } if(len>n)len=n; if(cnt>K)cnt=K; up(f[o^1][k][len][cnt],w+(!j&&k)); } } } for(j=0;j<2;j++)for(x=0;x<=n;x++)for(y=0;y<=K;y++){ if(y+(x==n)>=K) up(ans,f[o][j][x][y]); } if(ans==inf)ans=-1; printf("%d",ans); } //f[i][fliped?][len][cnt]
B. Exciting Finish!
设$f[S][i][j]$表示已经给$S$集合的人加了分,当前最高分的人是$i$,一共加了$j$分的方案数。
那么枚举下一个加分的人$k$,则$k$加的分为$q=max(p_i+1-p_k,0)$,因为要求$q$不下降,因此后面所有人都至少加了$q$,把它全部算掉之后,剩下的人(包括$k$)的分差可以等效为初始的$p$的差值,故不需要记录最高分具体是多少以及$q$具体是多少。
时间复杂度$O(2^nn^2x)$。
#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 = 12, 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, X; int a[N]; int f[1 << N][N][707]; int dfs(int msk, int pre, int X, int rstnum) { if (!rstnum)return 1; if (~f[msk][pre][X])return f[msk][pre][X]; int &rtn = f[msk][pre][X]; rtn = 0; for (int i = 0; i < n; ++i)if(msk >> i & 1)if(i != pre) { int add = max(a[pre] + 1 - a[i], 0); if (X >= rstnum * add) { rtn += dfs(msk ^ 1 << i, i, X - rstnum * add, rstnum - 1); } } return rtn; } int main() { while (~scanf("%d%d", &n, &X)) { for (int i = 0; i < n; ++i) { scanf("%d", &a[i]); } sort(a, a + n); MS(f, -1); printf("%d ", dfs((1 << n) - 1, n - 1, X, n)); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 【数据】 */
C. Flipping Out
首先建出AC自动机,将$n-1$个已知串的贡献全部删除。
再对母串差分,可以将信息表示成“丢失串是否是母串的某个前缀的后缀”。
将串和差分数组翻转,信息则变为“丢失串是否是母串的某个后缀的前缀”。
若全部都是“否”,则方案数显然为无穷。
否则随便找到一个“是”的后缀$x$,对于其它每个后缀,求出LCP,那么可以更新$x$可取前缀的取值范围的上下界。
最后枚举每个可能的丢失串,利用Hash判断是否在$n-1$个串中出现过即可。
时间复杂度$O(nlog n)$。
#include<cstdio> #include<cstring> #include<algorithm> #include<set> using namespace std; typedef unsigned long long ll; const int N=1000010,M=233; int n,len,i,x; int s[N]; char a[N]; int tot,son[N][2],v[N],fail[N]; set<ll>T; int ans,L,R; ll p[N],f[N]; inline void ins(){ static char s[N]; scanf("%s",s); int l=strlen(s),i; for(i=0;i<l;i++)s[i]=s[i]=='H'; for(int x=i=0,w;i<l;i++){ w=s[i]; if(!son[x][w])son[x][w]=++tot; x=son[x][w]; if(i==l-1)v[x]^=1; } ll t=0; for(i=l-1;~i;i--)t=t*M+s[i]+1; T.insert(t); } void make(){ int h=1,t=0,i,j,x; static int q[N]; fail[0]=-1; for(i=0;i<2;i++)if(son[0][i])q[++t]=son[0][i]; while(h<=t){ for(x=q[h++],i=0;i<2;i++) if(son[x][i]){ fail[son[x][i]]=son[fail[x]][i]; v[son[x][i]]^=v[fail[son[x][i]]]; q[++t]=son[x][i]; }else son[x][i]=son[fail[x]][i]; } } inline ll ask(int l,int r){return f[r]-f[l-1]*p[r-l+1];} inline int lcp(int x,int y){ int l=1,r=min(len-x+1,len-y+1),mid,t=0; while(l<=r){ mid=(l+r)>>1; if(ask(x,x+mid-1)==ask(y,y+mid-1))l=(t=mid)+1;else r=mid-1; } return t; } int main(){ scanf("%d",&n); for(i=1;i<n;i++)ins(); make(); scanf("%s",a+1); if(a[1]=='H')return puts("0"),0; //T 0 H 1 len=strlen(a+1); for(i=1;i<=len;i++)a[i]=a[i]=='H'; len--; for(i=1;i<=len;i++)s[i]=a[i]^a[i+1]; for(i=1,x=0;i<=len;i++){ x=son[x][a[i]]; s[i]^=v[x]; } reverse(a+1,a+len+1); reverse(s+1,s+len+1); for(i=1;i<=len;i++)if(s[i])break; if(i>len)return puts("-1"),0; x=i; L=1,R=len-x+1; for(p[0]=i=1;i<=len;i++)f[i]=f[i-1]*M+a[i]+1,p[i]=p[i-1]*M; //for(i=1;i<=len;i++)printf("%c",a[i]?'H':'T');puts(""); //for(i=1;i<=len;i++)printf("%d",s[i]);puts(""); //printf("x=%d ",x); for(i=1;i<=len;i++){ int t=lcp(i,x); //printf("%d %d ",i,t); if(s[i]){ R=min(R,t); }else{ L=max(L,t+1); } } //printf("%d %d ",L,R); for(i=L;i<=R;i++)if(T.find(ask(x,x+i-1))==T.end())ans++; printf("%d",ans); } /* THTTHTT 0000100 i肯定是1 一定是[1,i]的某个后缀 枚举0 那么它和i的最长公共后缀之后的都不行 枚举1 那么只能是它最长公共后缀 */
D. Jumping Haybales
BFS求出左上角到每个点的最短路,对于每行每列分别开一个并查集来跳过所有已经访问过的点。
时间复杂度$O(n^2alpha(n))$。
#include<cstdio> const int N=2010; int n,k,i,d[N][N],h=1,t,q[N*N][2]; char a[N][N]; struct DSU{ int f[N]; void init(){ for(int i=1;i<=n+1;i++)f[i]=i; } int F(int x){return f[x]==x?x:f[x]=F(f[x]);} }down[N],right[N]; inline void ext(int x,int y,int w){ if(a[x][y]=='#')return; if(d[x][y])return; d[x][y]=w; q[++t][0]=x; q[t][1]=y; } int main(){ scanf("%d%d",&n,&k); for(i=1;i<=n;i++)scanf("%s",a[i]+1); for(i=1;i<=n;i++)down[i].init(),right[i].init(); ext(1,1,1); while(h<=t){ int x=q[h][0]; int y=q[h++][1]; int w=d[x][y]+1; //right for(i=right[x].F(y);i<=y+k&&i<=n;i=right[x].F(i)){ ext(x,i,w); right[x].f[i]++; } //down for(i=down[y].F(x);i<=x+k&&i<=n;i=down[y].F(i)){ ext(i,y,w); down[y].f[i]++; } } printf("%d",d[n][n]-1); }
F. Move Away
首先考虑求出一个可行解:它必然是两个圆的交点或者圆上任意一点。
求出可行解之后,在这个基础上二分答案$mid$,若以原点为圆心,半径为$mid$的圆(内部不算)与其它所有圆(内部要算)的交集非空,则可行。
同理,交集的可行解只可能是半径为$mid$的圆上任意一点或者它和某个圆的交点,暴力检查即可。
#include<cstdio> #include<algorithm> #include<cmath> using namespace std; const double eps=1e-9,inf=1e20; inline int sig(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 v){return P(x+v.x,y+v.y);} P operator-(P v){return P(x-v.x,y-v.y);} P operator*(double v){return P(x*v,y*v);} P operator/(double v){return P(x/v,y/v);} 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;} P trunc(double l){return (*this)*l/len();} P rot90(){return P(-y,x);} }; struct C{ P c;double r; C(){c=P(0,0),r=0;} C(P _c,double _r){c=_c,r=_r;} }a[2010]; inline bool circle_circle_intersection(C a,C b,P&p,P&q){ double d=(a.c-b.c).len(); if(d>a.r+b.r)return 0; if(d<fabs(a.r-b.r))return 0; double l=((a.c-b.c).len_sqr()+a.r*a.r-b.r*b.r)/(2*d),h=sqrt(a.r*a.r-l*l); P vl=(b.c-a.c).trunc(l),vh=vl.rot90().trunc(h); p=a.c+vl+vh; q=a.c+vl-vh; return 1; } int n,i,j,flag,ia,ib; double l,r,mid; double L,R,MID,t0,t1,D,U,A,B,x,y; inline bool ok(P b){ for(int i=0;i<n;i++)if(sig((b-a[i].c).len()-a[i].r)>0)return 0; return 1; } inline void gao(P b){ if(!ok(b))return; l=max(l,b.len()); } inline bool check(double R){ a[n].r=R; if(ok(P(R,0)))return 1; for(int i=0;i<n;i++){ P a,b; if(circle_circle_intersection(::a[i],::a[n],a,b)){ if(ok(a))return 1; if(ok(b))return 1; } } return 0; } int main(){ for(scanf("%d",&n);i<n;i++)scanf("%lf%lf%lf",&a[i].c.x,&a[i].c.y,&a[i].r); for(i=0;i<n;i++)gao(P(a[i].c.x-a[i].r,a[i].c.y)); for(i=0;i<n;i++)for(j=0;j<i;j++){ P a,b; if(circle_circle_intersection(::a[i],::a[j],a,b)){ gao(a); gao(b); } } r=1e4; for(int _=100;_;_--){ if(check(mid=(l+r)/2))l=mid;else r=mid; } printf("%.3f",l); }
I. Star Arrangements
按题意模拟即可。
#define ms(x, y) memset(x, y, sizeof(x)) #define mc(x, y) memcpy(x, y, sizeof(x)) #define lson o << 1, l, mid #define rson o << 1 | 1, mid + 1, r #define ls o << 1 #define rs o << 1 | 1 #include<stdio.h> #include<string.h> #include<math.h> #include<queue> #include<map> #include<stack> #include<vector> #include<list> #include<set> #include<string> #include<algorithm> #pragma comment(linker,"/STACK:102400000,102400000") template <class T> inline void gmax(T &a, T b){if(b > a) a = b;} template <class T> inline void gmin(T &a, T b){if(b < a) a = b;} using namespace std; const int N = 1e5 + 10, M = 2e6 + 10, Z = 1e9 + 7, maxint = 2147483647, ms1 = 16843009, ms31 = 522133279, ms63 = 1061109567, ms127 = 2139062143; const double PI = acos(-1.0), eps = 1e-8; typedef long long LL; void fre() { freopen("/Users/luras/Desktop/in.txt", "r", stdin); freopen("/Users/luras/Desktop/out.txt", "w", stdout); } const int INF = 1e9; int casenum, casei; int n; bool c1(int i) { if(n % (2 * i - 1)) return 0; int x = n / (2 * i - 1); if(x > 0) return 1; return 0; } bool c2(int i) { if((n - 1 + i) % (2 * i - 1)) return 0; int x = (n - 1 + i) / (2 * i - 1); if(x > 1) return 1; return 0; } bool c3(int i) { if(n % (2 * i)) return 0; int x = n / (2 * i); if(x > 0) return 1; return 0; } bool c4(int i) { if((n + i) % (2 * i)) return 0; int x = (n + i) / (2 * i); if(x > 1) return 1; return 0; } int main() { scanf("%d", &n); for(int i = 2; i <= n; i ++){ if(c1(i) || c2(i)){ printf("%d %d ", i, i - 1); } if(c3(i) || c4(i)){ printf("%d %d ", i, i); } } return 0; } /* 题意: 类型: 分析: 优化: trick: 数据: Sample Input Sample Output */
J. Treasure Map
设$f[i][j]$表示第$i$天位于$j$点的最大收益,暴力转移即可。
时间复杂度$O(1000(n+m))$。
#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 = 1010, 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; int g[N], d[N]; vector<pair<int,int> >a[N]; int f[N][N]; int val(int p, int day) { return max(0, g[p] - d[p] * (day - 1)); } int main() { while (~scanf("%d%d", &n, &m)) { for (int i = 1; i <= n; ++i) { scanf("%d%d", &g[i], &d[i]); a[i].clear(); } for (int i = 1; i <= m; ++i) { int x, y, z; scanf("%d%d%d", &x, &y, &z); a[x].push_back({ y,z }); a[y].push_back({ x,z }); } //printf("edge:%d %d ", a[1][0].first, a[1][0].second); MS(f, -63); f[1][1] = val(1, 1); int ans = f[1][1]; for (int i = 1; i <= 1000; ++i) { for (int x = 1; x <= n; ++x)if(f[i][x] >= 0) { // //printf("f[%d][%d] = %d ", i, x, f[i][x]); // for (auto it : a[x]) { int y = it.first; int z = it.second; int day = min(i + z, 1005); // //printf("%d %d %d ", y, z, day); // gmax(f[day][y], f[i][x] + val(y, day)); gmax(ans, f[day][y]); } } } //for (int i = 1; i <= n; ++i)gmax(ans, f[1001][i]); printf("%d ", ans); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 【数据】 */