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

Claris發表於2017-11-18

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&&吐槽】


【題意】


【分析】


【時間複雜度&&優化】


【資料】


*/

  

相關文章