Kick Start 2019 round D 题解

整体来说这次的题不是很难,我都能自己想出来,并写完,但是这次马力有点大,C题写了2hrs…

A.X or What?

这题比较简单,重点在一个 xor的性质,与位的异或次序无关即两数 A , B A,B A,B K K K

⋀ i A i ∧ B i = ( ⋀ i A i ) ∧ ( ⋀ i B i ) \bigwedge_i A_i \wedge B_i = (\bigwedge_i A_i)\wedge (\bigwedge_i B_i) iAiBi=(iAi)(iBi)

code: github

B.Latest Guests

tips 1: M > N M>N M>N 是无效的,因为仅有最后被访问的才会被记录,所以如果对于 领地 k k k 它访问了两次,那仅仅需要知道最后一次访问的时间就行了
tips 2: 因为仅有最后一次被访问才会被记录,即时间戳越后的优先于时间戳越早的,因此我们可以倒过来模拟整个过程,即从 G i G_i Gi 项最后一次待的地方开始往前模拟。

即有如下伪代码:

vis[] // 第 i 个东西最后被访问的时间戳

g[i] // cur,当前位置,dir行动方向,ans,访问次数,

for time = M to 0:
    if(vis[g[i].cur] <=time) // 还未被访问过
        vis[g[i].cur] = time
        g[i].ans ++
    update g[i].cur

接下来就是优化模拟的过程了,

tips 3: 任意的项 G i G_i Gi 仅由当前位置 c u r cur cur ,和行进方向 d i r dir dir 决定它的ans,因此可以合并所有 c u r cur cur d i r dir dir 相同的项到一组。

tips 4: 可以用一个 访问的位置数目 n o _ v i s no\_vis no_vis 来记录还有多少位置未访问,到了 0就退出.

前4项技巧已经能够通过test 1和test 2 了,
这是我原始的代码

#include

using namespace std;

#define ms(x,v) memset(x,(v), sizeof(x))
#define msn(x,v,n) memset(x,(v),sizeof(x[0]) * n)
#define INF 0x3f3f3f3f

typedef long long LL;
typedef pair<int,int> PII;

const int MAXN = 2e5 +10;

struct Node{
    int cur,dir,ans;
    vector<int> ids;
};


void solve(){
    int N,G,M;
    cin >> N >> G>>M;
    vector<vector<int> > guests(2*N);

    for(int i=0 ; i< G; ++i){
        int s;
        char x;
        cin >> s>>x;
        s--;// map to [0,N)
        int dir = x == 'C' ? 1 : -1;
        s = (s + dir * (M%N) +N) % N;
        int idx = (s<<1) + (dir==1);
        guests[idx].push_back(i);
    }

    vector<Node> gg;
    for(int i=0 ; i<guests.size() ; ++i){
        if(guests[i].empty())continue;
        Node tmp = {
            i>>1,
            i&1 ? -1 : 1, // anti-dir
            0,
            guests[i],
        };
        gg.push_back(tmp);
    }
    vector<int> timep(N,-1);
    int not_vis = N;
    if(M > N) M = N;
    for(int time = M ; time >= 0 && not_vis>0 ; --time)
    {
        for(int i=0 ; i< gg.size() ; ++i){
            int & cur = gg[i].cur;
            if(timep[cur]<time){
                not_vis--;
                timep[cur] = time;
            }//first access
            if(timep[cur] <=time){
                gg[i].ans ++;
            }
            gg[i].cur= (cur + gg[i].dir + N) % N;
        }
    }
    vector<int> ans(G);
    for(auto e: gg){
        for(auto id : e.ids)
            ans[id] = e.ans;
    }
    for(int i=0 ; i < ans.size(); ++i)
            cout << " " << ans[i];
    
}

int main(int argc, char const *argv[])
{   
    
    ios :: sync_with_stdio(0);
    cin.tie(0);
    // cout.tie(0);
    std::cout.precision(8);
    std::cout.setf( std::ios::fixed, std:: ios::floatfield );
    int T;
    cin >> T;
    
    for(int kase =1;  kase <= T ; ++kase)
    {   
        cout << "Case #"<<kase << ":";

        solve();
        cout << '\n';
    }

    return 0;
}

(其实是可以被hack的…,最坏情况下仍旧需要 O ( M G ) O(M G) O(MG)) 不过估计这个比较随机

tips 5: 延续tips 3的想法,任意一项它的访问路径仅仅由 c u r , d i r cur,dir cur,dir 决定,因此如果某个状态 c u r , d i r cur,dir cur,dir 已经出现过了,那么这一项就不会被记录了。用个array记录一个这个值,由于 d i r dir dir 仅仅有 2 2 2
因此,状态最多 2 N 2N 2N
所以复杂度 O ( 2 N l o g ( N ) ) O(2N log(N)) O(2Nlog(N)) (实际中我用了set来记录还能更新的集合)

code: github

C.Food Stalls

这题也比较简单,只是编码有点复杂

tips 1: 按 X i X_i Xi 的大小给这 n n n 个点排序,如果固定warehouse 的位置 i i i, 那么选取 X j > X i X_j > X_i Xj>Xi 的点的花费是 X j − X i + C j X_j - X_i + C_j XjXi+Cj,选取 X j < X i X_j < X_i Xj<Xi 的花费是 X i − X j + C j X_i - X_j + C_j XiXj+Cj, 因此对于这 n n n 个点,任意固定 warehouse 的位置后,计算上面的值,选前 k k k 个求和就是答案。

伪代码:

for warehouse = 0 to N:
    for j = 0 to N:
        计算每个点的花费
    选取前 $k$ 个求和

这是 ok的只是复杂度太大了, O ( N 2 ) O(N^2) O(N2),我们可以优化一下

如果我们维护两个集合对于 X j > X i X_j > X_i Xj>Xi 计算 upper_set(X_j+C_j), 对于 X i > X j X_i > X_j Xi>Xj 的 计算 lower_set(-X_j+C_j) , 有了这两个集合后我们也能计算出答案。同时想象一下 warehouse 从i -> i+1 的过程,显然,第 i i i 项会从 upper_set 移到 lower,同时还需要维护一个第 k k k 项的分界点(这个贡献意义上) 的位置,简单想一下,维护这个不可能每次都经过 O ( N ) O(N) O(N) 次,虽然我不会算它的复杂度,但在平均意义上我觉得仅仅会移动 O(1) 次,因此复杂度是 O ( N l o g N ) O(N logN) O(NlogN) 左右一定能通过

code : github

版权声明

本作品为作者原创文章,采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

作者: taotao

转载请保留此版权声明,并注明出处

你可能感兴趣的:(kickstart,interview,算法刷题,google,kickstart,18/19)