n*m个位置,刚开始是空的,每个位置最多安排一个人。
假如已经安排了x个人,第x+1个人无法安排位置的充要条件是没有空位置或每个空位置的相邻位置都已经安排了人。
两个位置相邻等价于两个位置的曼哈顿距离是1。
多组测例。
数据范围:
首先想了一下网络流,不行。
然后考虑状压dp,每行的状态用三进制表示。
0表示当前位置是空的,需要正下方左一个人使此位置不能做人。
1表示当前位置被覆盖(相邻位置已经有人了),此时的相邻指的是上方、左方、右方,因为下方的状态未知。
2表示当前位置坐人。
但是 ,这样显然是会T的。
考虑如下优化:
(1)同一行不能出现两个相邻的0。因为这两个0要想都被覆盖,显然只能这两个0下方都是2,但是两个2不能相邻。
(2)同一行不能出现两个相邻的2。
(3)同一行不能出现相邻的0和2。显然这个0会被覆盖变成1。
(4)同一行出现两个相邻的1,但是这两个1的左边和右边都没有2,因为已经被覆盖了,所以只能上方的两个位置都是2,但两个2不能相邻,所以这种情况还是不合法的。
这种情况下当m=15时,状态数大约是6500。
第一行和最后一行的状态需要特判。
n=1时也要特判。
因为m只有15而测例有1000个,所以把测例离线,把m相同的测例放在一起去dp会大大减少时间复杂度。
m确定的时间复杂度:,s是m确定时的状态数。
这题比赛时候只有一个人过了,因为这题细节多如牛毛,在比赛中通过需要很强的码力。
我猜很多人想到了状压dp,但是写挂了。
其实可以写到200行以内,但是我不想优化了,直接无脑判断各种case。
题解是秒懂的,然后我写了四个多小时,虽然最后1A了,不过过程极其自闭。
#include
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define cl(x) x.clear()
#define all(x) x.begin() , x.end()
#define rep(i , x , n) for(int i = x ; i <= n ; i ++)
#define per(i , n , x) for(int i = n ; i >= x ; i --)
#define mem0(x) memset(x , 0 , sizeof(x))
#define mem_1(x) memset(x , -1 , sizeof(x))
#define mem_inf(x) memset(x , 0x3f , sizeof(x))
#define debug(x) cerr << '*' << x << '\n'
#define ddebug(x , y) cerr << '*' << x << ' ' << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair pii ;
typedef pair pll ;
const int mod = 998244353 ;
const int maxn = 1000 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count()) ;
int t ;
int cnt0[20][7000] , cnt2[20][7000] ;
vector nxt[20][7000] ;
struct query
{
int n , m , id ;
int ans ;
string s[maxn] ;
bool operator < (const query &s) const
{
if(m != s.m) return m < s.m ;
else if(n != s.n) return n < s.n ;
else return id < s.id ;
}
} Q[maxn] ;
vector v[20] ;
bool ok(int step , int sum , int up)
{
int z1 = -1 , z2 = -1 , z3 = -1 , z4 ;
if(step >= 2)
{
z1 = sum % 3 , sum /= 3 ;
z2 = sum % 3 , sum /= 3 ;
z3 = sum % 3 , sum /= 3 ;
z4 = sum % 3 , sum /= 3 ;
if(z1 == 0 && z2 == 0) return 0 ;
if(z1 == 0 && z2 == 2) return 0 ;
if(z1 == 2 && z2 == 0) return 0 ;
if(z1 == 2 && z2 == 2) return 0 ;
if(z1 == 1 && z2 == 1)
{
if(up == 2) return 0 ;
if(step == up)
{
if(z3 != 2) return 0 ;
}
}
if(step >= 3)
{
if(z2 == 1 && z3 == 1)
{
bool flag1 = 0 ;
bool flag2 = 0 ;
if(z1 == 2) flag1 = 1 ;
if(step >= 4)
{
if(z4 == 2) flag2 = 1 ;
}
return flag1 || flag2 ;
}
}
}
return 1 ;
}
void dfs(int step , int sum , int up)
{
if(!ok(step , sum , up)) return ;
if(step == up)
{
v[up].pb(sum) ;
return ;
}
rep(i , 0 , 2) dfs(step + 1 , sum * 3 + i , up) ;
}
int pp[20] ;
void init()
{
rep(i , 1 , 15) dfs(0 , 0 , i) ;
rep(i , 1 , 15)
{
int siz = sz(v[i]) ;
rep(j , 0 , siz - 1)
{
int temp = v[i][j] ;
int u = temp ;
rep(k , 1 , i)
{
int x = u % 3 ;
u /= 3 ;
if(x == 0) cnt0[i][j] ++ ;
if(x == 2) cnt2[i][j] ++ ;
}
}
}
rep(i , 1 , 15)
{
int siz = sz(v[i]) ;
rep(j , 0 , siz - 1) rep(k , 0 , siz - 1)
{
bool flag = 1 ;
int u = v[i][j] ;
int x = v[i][k] ;
int t3 = -1 ;
rep(j , 1 , i)
{
int t1 = u % 3 ;
int t2 = x % 3 ;
u /= 3 , x /= 3 ;
if(t1 == 0 && t2 != 2)
{
flag = 0 ;
break ;
}
if(t1 == 2 && t2 != 1)
{
flag = 0 ;
break ;
}
if(t1 == 1 && t2 == 1)
{
int t4 = -1 ;
if(j < i) t4 = x % 3 ;
if(t4 != 2 && t3 != 2)
{
flag = 0 ;
break ;
}
}
t3 = t2 ;
}
if(flag == 1) nxt[i][j].pb(k) ;
}
}
}
int min1[7000] , pre[maxn][7000] ;
queue q ;
vector kk ;
bool match(bool b , int m , int x)
{
if(!b) return 1 ;
cl(kk) ;
rep(i , 1 , m) kk.pb(x % 3) , x /= 3 ;
if(m == 1)
{
if(m == 1 && kk[0] == 1) return 0 ;
return 1 ;
}
if(m == 2)
{
if(kk[0] == 1 && kk[1] != 2) return 0 ;
if(kk[0] != 2 && kk[1] == 1) return 0 ;
return 1 ;
}
if(kk[0] == 1 && kk[1] != 2) return 0 ;
rep(i , 1 , m - 2) if(kk[i] == 1 && kk[i - 1] != 2 && kk[i + 1] != 2) return 0 ;
if(kk[m - 1] == 1 && kk[m - 2] != 2) return 0 ;
return 1 ;
}
void bfs(int step , int m , bool b)
{
int siz = sz(q) ;
mem_inf(min1) ;
while(siz --)
{
int now = q.front().fi ;
int cnt = q.front().se ;
q.pop() ;
if(b)
{
int ss = sz(v[m]) ;
rep(i , 0 , ss - 1)
{
if(match(1 , m , v[m][i]) && cnt + cnt2[m][i] < min1[i]) min1[i] = cnt + cnt2[m][i] , pre[step][i] = now ;
}
}
else for(int u : nxt[m][now]) if(cnt + cnt2[m][u] < min1[u]) min1[u] = cnt + cnt2[m][u] , pre[step][u] = now ;
}
siz = sz(v[m]) ;
rep(i , 0 , siz - 1) if(min1[i] != inf) q.push({i , min1[i]}) ;
}
void draw(int i , int now , int m , int x)
{
if(now == 0) return ;
//debug(v[m][x]) ;
//ddebug(now , min1[x]) ;
Q[i].s[now] = "" ;
int temp = x ;
x = v[m][x] ;
rep(j , 1 , m)
{
if(x % 3 == 2) Q[i].s[now] += '*' ;
else Q[i].s[now] += '.' ;
x /= 3 ;
}
draw(i , now - 1 , m , pre[now][temp]) ;
}
void solve1(int now , int i , int m)
{
int up = sz(v[m]) ;
int temp = -1 ;
Q[i].ans = inf ;
rep(j , 0 , up - 1) if(cnt0[m][j] == 0 && min1[j] < Q[i].ans && match(Q[i].n == 1 , m , v[m][j])) Q[i].ans = min1[j] , temp = j ;
draw(i , now , m , temp) ;
}
void solve(int i , int j , int m)
{
int now = 0 ;
bool flag = 1 ;
while(!q.empty()) q.pop() ;
q.push({0 , 0}) ;
rep(k , i , j)
{
while(now < Q[k].n)
{
now ++ ;
bfs(now , m , flag) ;
flag = 0 ;
}
solve1(now , k , m) ;
}
}
int main()
{
ios ;
init() ;
cin >> t ;
rep(i , 1 , t) cin >> Q[i].n >> Q[i].m , Q[i].id = i ;
sort(Q + 1 , Q + t + 1) ;
rep(i , 1 , t)
{
int j = i ;
while(j + 1 <= t && Q[j + 1].m == Q[j].m) j ++ ;
solve(i , j , Q[j].m) ;
i = j ;
}
sort(Q + 1 , Q + t + 1 , [](const query& a , const query& b) {return a.id < b.id ;}) ;
rep(i , 1 , t)
{
cout << "Case #" << i << ": " << Q[i].ans << '\n' ;
rep(j , 1 , Q[i].n) cout << Q[i].s[j] << '\n' ;
}
return 0 ;
}
n个无限循环字符串,每个字符串都给出循环节s。
求这n个串的本质不同公共子串的个数。
首先需要对无限循环串进行两步预处理:
(1)找到最小循环节。
(2)转换为最小循环同构。
然后有这个引理:
假如预处理后每个串都一样就代表有无穷多个本质不同公共子串。
把n-1个串和最短的串分别求出公共子串,然后求交集就是n个串的本质不同公共子串。
根据上面的引理,把最短串复制到最长串长度的四倍,然后剩余n-1个串每个串复制到自身的四倍,插入到广义sam。
在广义sam上打标记就知道n个串的本质不同公共子串个数了。
#include
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define cl(x) x.clear()
#define all(x) x.begin() , x.end()
#define rep(i , x , n) for(int i = x ; i <= n ; i ++)
#define per(i , n , x) for(int i = n ; i >= x ; i --)
#define mem0(x) memset(x , 0 , sizeof(x))
#define mem_1(x) memset(x , -1 , sizeof(x))
#define mem_inf(x) memset(x , 0x3f , sizeof(x))
#define debug(x) cerr << '*' << x << '\n'
#define ddebug(x , y) cerr << '*' << x << ' ' << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair pii ;
typedef pair pll ;
const int mod = 998244353 ;
const int maxn = 2e6 + 10 ;
const int maxn2 = 3e5 + 10 ;
const int maxm = 26 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count()) ;
string s[maxn2] , t ;
struct Sam
{
int last , cnt ;
int tr[maxn << 1][maxm] , nxt[maxn << 1] ;
int len[maxn << 1] ;
int val[maxn << 1] ;
int vis[maxn << 1] ;
queue q ;
void init()
{
last = cnt = 1 ;
mem0(tr) , mem0(nxt) , mem0(len) ;
while(!q.empty()) q.pop() ;
}
void add(int c)
{
int p = last , np = ++ cnt ;
last = np , len[np] = len[p] + 1 ;
for( ; p && !tr[p][c] ; p = nxt[p]) tr[p][c] = np ;
if(!p) nxt[np] = 1 ;
else
{
int q = tr[p][c] ;
if(len[p] + 1 == len[q]) nxt[np] = q ;
else
{
int nq = ++ cnt ;
len[nq] = len[p] + 1 ;
memcpy(tr[nq] , tr[q] , sizeof(tr[q])) ;
nxt[nq] = nxt[q] , nxt[q] = nxt[np] = nq ;
for( ; tr[p][c] == q ; p = nxt[p]) tr[p][c] = nq ;
}
}
}
void build(string s)
{
int Len = sz(s) ;
last = 1 ;
rep(i , 0 , Len - 1) add(s[i] - 'a') ;
}
void calc(int n)
{
rep(i , 1 , n)
{
int len = sz(s[i]) ;
int p = 1 ;
rep(j , 0 , len - 1)
{
p = tr[p][s[i][j] - 'a'] ;
int u = p ;
while(vis[u] != i && u != 1) vis[u] = i , val[u] ++ , u = nxt[u] ;
}
}
ll ans = 0 ;
rep(i , 1 , cnt) if(val[i] == n) ans += len[i] - len[nxt[i]] ;
cout << ans << '\n' ;
}
} sam ;
struct Min_str
{
int sec[maxn2] ;
int solve(int n)
{
int k = 0 , i = 0 , j = 1 ;
while(k < n && i < n && j < n)
{
if(sec[(i + k) % n] == sec[(j + k) % n]) k ++ ;
else
{
sec[(i + k) % n] > sec[(j + k) % n] ? i = i + k + 1 : j = j + k + 1 ;
if(i == j) i ++ ;
k = 0 ;
}
}
i = min(i , j) ;
return i ;
}
} min_str ;
char s2[maxn2] ;
int len2 ;
int nxt[maxn2] ;
void get_nxt()
{
nxt[0] = -1 ;
int j = 0 , k = -1 ;
while(j < len2)
{
if(k == -1 || s2[j] == s2[k]) j ++ , k ++ , nxt[j] = k ;
else k = nxt[k] ;
}
}
bool cmp(string s1 , string s2)
{
return sz(s1) < sz(s2) ;
}
int main()
{
ios ;
int n ;
cin >> n ;
rep(i , 1 , n)
{
cin >> s[i] ;
len2 = sz(s[i]) ;
rep(j , 0 , len2 - 1) s2[j] = s[i][j] ;
get_nxt() ;
if(len2 % (len2 - nxt[len2]) == 0) s[i] = s[i].substr(0 , len2 - nxt[len2]) ;
len2 = sz(s[i]) ;
rep(j , 0 , len2 - 1) min_str.sec[j] = s[i][j] ;
int x = min_str.solve(len2) ;
t = "" ;
rep(j , 0 , len2 - 1) t += s[i][(j + x) % len2] ;
s[i] = t ;
}
sort(s + 1 , s + n + 1 , cmp) ;
rep(i , 2 , n) rep(j , 1 , 2) s[i] += s[i] ;
t = s[1] ;
while(sz(s[1]) < sz(s[n])) s[1] += t ;
bool flag = 1 ;
rep(i , 2 , n) if(s[i] != s[i - 1]) flag = 0 ;
if(flag) { cout << "-1\n" ; return 0 ; }
sam.init() ;
rep(i , 1 , n) sam.build(s[i]) ;
sam.calc(n) ;
return 0 ;
}