2020牛客暑期多校训练营(第八场)

比赛链接

A

一、题意

二、题解

三、代码

B

一、题意

二、题解

三、代码

C

一、题意

n*m个位置,刚开始是空的,每个位置最多安排一个人。

假如已经安排了x个人,第x+1个人无法安排位置的充要条件是没有空位置或每个空位置的相邻位置都已经安排了人。

两个位置相邻等价于两个位置的曼哈顿距离是1。

多组测例。1\leqslant T\leqslant 1000

数据范围:1\leqslant n\leqslant 1000,1\leqslant m\leqslant 15

二、题解

首先想了一下网络流,不行。

然后考虑状压dp,每行的状态用三进制表示。

0表示当前位置是空的,需要正下方左一个人使此位置不能做人。

1表示当前位置被覆盖(相邻位置已经有人了),此时的相邻指的是上方、左方、右方,因为下方的状态未知。

2表示当前位置坐人。

但是 3^{15}\approx 10^7,这样显然是会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确定的时间复杂度:O(n*s^2),s是m确定时的状态数。

三、感想

这题比赛时候只有一个人过了,因为这题细节多如牛毛,在比赛中通过需要很强的码力。

我猜很多人想到了状压dp,但是写挂了。

其实可以写到200行以内,但是我不想优化了,直接无脑判断各种case。

题解是秒懂的,然后我写了四个多小时,虽然最后1A了,不过过程极其自闭。

四、代码

#include<bits/stdc++.h>
#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<int , int> pii ;
typedef pair<ll , ll> 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<int> 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<int> 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<pii> q ;
vector<int> 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 ;
}

D

一、题意

二、题解

三、代码

E

一、题意

二、题解

三、代码

F

一、题意

二、题解

三、代码

G

一、题意

二、题解

三、代码

H

一、题意

n个无限循环字符串,每个字符串都给出循环节s。

求这n个串的本质不同公共子串的个数。

数据范围:2\leqslant n\leqslant 300000,1\leqslant \sum \left| s\right | \leqslant 300000

二、题解

首先需要对无限循环串进行两步预处理:

(1)找到最小循环节。

(2)转换为最小循环同构。

然后有这个引理:

假如预处理后每个串都一样就代表有无穷多个本质不同公共子串。

把n-1个串和最短的串分别求出公共子串,然后求交集就是n个串的本质不同公共子串。

根据上面的引理,把最短串复制到最长串长度的四倍,然后剩余n-1个串每个串复制到自身的四倍,插入到广义sam。

在广义sam上打标记就知道n个串的本质不同公共子串个数了。

三、代码

#include<bits/stdc++.h>
#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<int , int> pii ;
typedef pair<ll , ll> 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<int> 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 ;
}

I

一、题意

二、题解

三、代码

J

一、题意

二、题解

三、代码

K

一、题意

二、题解

三、代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值