2017-2018 ACM-ICPC Southeast Regional Contest (Div. 1)

A. Ducks in a Row

当$n\times 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\n", dfs((1 << n) - 1, n - 1, X, n));
}
return 0;
}
/*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 【数据】 */

  

C. Flipping Out

首先建出AC自动机,将$n-1$个已知串的贡献全部删除。

再对母串差分,可以将信息表示成“丢失串是否是母串的某个前缀的后缀”。

将串和差分数组翻转,信息则变为“丢失串是否是母串的某个后缀的前缀”。

若全部都是“否”,则方案数显然为无穷。

否则随便找到一个“是”的后缀$x$,对于其它每个后缀,求出LCP,那么可以更新$x$可取前缀的取值范围的上下界。

最后枚举每个可能的丢失串,利用Hash判断是否在$n-1$个串中出现过即可。

时间复杂度$O(n\log 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\n",x);
for(i=1;i<=len;i++){
int t=lcp(i,x);
//printf("%d %d\n",i,t);
if(s[i]){
R=min(R,t);
}else{
L=max(L,t+1);
}
}
//printf("%d %d\n",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^2\alpha(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\n", i, i - 1);
}
if(c3(i) || c4(i)){
printf("%d %d\n", 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\n", 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\n", 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\n", 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\n", ans);
}
return 0;
}
/*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 【数据】 */

  

上一篇:Spring Security(一):官网向导翻译


下一篇:Android开发中使用七牛云存储进行图片上传下载