Codeforces Round #578 (Div. 2) Solution

Problem A Hotelier

直接模拟即可~~

复杂度是$O(10 \times n)$

# include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
char s[N];
bool a[10];
int main()
{
    int n; scanf("%d",&n); scanf("%s",s);
    for (int i=0;i<n;i++) {
        if (s[i]=='L') {
            for (int j=0;j<=9;j++)
             if (!a[j]) { a[j]=true; break;}
        } else if (s[i]=='R') {
            for (int j=9;j>=0;j--)
             if (!a[j]) { a[j]=true; break;}
        } else a[s[i]-'0']=0;
    }
    for (int i=0;i<=9;i++) printf("%d",a[i]);
    puts("");
    return 0;
} 
A.cpp

Problem B Hotelier

注意到高度的限制是带绝对值的,化简为$H_{i+1} - k \leq H_i \leq H_{i+1}+k$

显然较高的限制是没有意义的,在一次最优的决策中不可能会选择当前从背包里面拿砖块去达到上界。

所以对于当前的决策来说,在保证仍然在高度下界以上,尽可能从地上拿砖块。

如果当前高度在高度下界以下,那么就需要从背包里拿出砖块使得到达高度下界。

注意到,地面的高度为$0$事实上,高度的下界是$\max\{H_{i+1} - k , 0\}$

复杂度是$O(T n)$

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=105;
int n,m,k,h[N];
signed main()
{
    int T; scanf("%lld",&T);
    while (T--) {
        scanf("%lld%lld%lld",&n,&m,&k);
        for (int i=1;i<=n;i++) scanf("%lld",&h[i]);
        bool flag=true;
        for (int i=1;i<n;i++) {
            int t=h[i]-max(h[i+1]-k,0ll);
            if (m+t>=0) { m+=t; continue;}
            else { flag=false; break;}
        }
        if (flag) puts("YES");else puts("NO");    
    }
    return 0;
}
B.cpp

Problem C Round Corridor

对于内部和外部的门,现在在一个连通块中是可以任意走动的,如果不在一个连通块中就不能。

对于图形可以划分成若干个连通块,当有重合的墙壁时会成为连通块的分界。第一个墙壁在12点钟方向已经重合。

非常明显接下来就是一个gcd类型的题目了。下面令$g = gcd(n,m)$

举例来说$[1,\frac{n}{g}] $和$[1,\frac{m}{g}]$ 是一个连通块.

形式化的说$[k \frac{n}{g}+1 , (k+1) \frac{n}{g}]$和$[k \frac{m}{g}+1 , (k+1) \frac{m}{g}] , k\in N $ 是一个连通块。

于是我们只需要判断如果在相同的连通块中就是"YES"否则就是"NO"

复杂度是$O(q)$

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n, m, q;
signed main()
{
    cin >> n >> m >> q;
    int lcmm = m / __gcd(n, m);
    int lcmn = n / __gcd(n, m);
    while (q --)
    {
        int sx, sy, ex, ey;
        cin >> sx >> sy >> ex >> ey;
        int p1 = 0, p2 = 0;
        if (sx == 1) p1 = (sy + lcmn - 1) / lcmn;
        else p1 = (sy + lcmm - 1) / lcmm;
        if (ex == 1) p2 = (ey + lcmn - 1) / lcmn;
        else p2 = (ey + lcmm - 1) / lcmm;
        if (p1 == p2) printf("YES\n");
        else printf("NO\n");
    }
}
C.cpp

Problem D White Lines

我们知道当前框的大小是不会发生变化的,对于一个左上角为$(i,j)$的正方形,覆盖到的范围是$(i,j) - (i+k-1,j+k-1)$

我们可以对行和列分别考虑,先考虑行,即求出在左上角为$(i,j)$的正方形点击,会造成多少行全是white。

对于每一行,我们记录最靠左黑点和最靠右白点的位置,只有覆盖这两个位置,该行才能被覆盖。

将已经全是白块的行忽略,这是因为无论怎么点击都是有贡献的。

首先可以暴力$O(k)$计算$(1,1)$的答案,于是问题转化为向下滑动这个$k\times k$的正方形来获取下一个答案。

在计算$(2,1) , (3,1)$等第一个安放的正方形的时候直接$O(k)$暴力计算,滑动一次判断进出的复杂度是$O(1)$的,

所以总复杂度是$O(n^2)$ 对于列的情况同理计算,答案就是同一位置两者之和的最大值。

# include<bits/stdc++.h>
using namespace std;
const int N=2e3+10;
struct rec{
    int l,r;
}c[N];
int a[N][N],n,k,ans[N][N];
char s[N];
int main()
{
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++) {
        scanf("%s",s+1);
        for (int j=1;j<=n;j++)
        if (s[j]=='B') a[i][j]=1;
        else a[i][j]=0;
    }
    int ret=0;
    for (int i=1;i<=n;i++) {
        int l=n+1,r=0;
        for (int j=1;j<=n;j++)
         if (a[i][j]==1) l=min(l,j),r=max(r,j);
        c[i].l=l; c[i].r=r;
        if (c[i].r<c[i].l) ret++;
    }
    int rec=ret;
    for (int j=1;j<=n-k+1;j++) {
        for (int i=1;i<=k;i++)
         if (c[i].l<=c[i].r && j<=c[i].l&&c[i].r<=j+k-1) ret++;
        ans[1][j]=ret;
        for (int i=2;i<=n-k+1;i++) {
            if (c[i-1].l<=c[i-1].r && j<=c[i-1].l && c[i-1].r<=j+k-1) ret--;
            if (c[i+k-1].l<=c[i+k-1].r && j<=c[i+k-1].l && c[i+k-1].r<=j+k-1) ret++;
            ans[i][j]=ret; 
        }
        ret=rec;
    }
    ret=0;
    for (int j=1;j<=n;j++) {
        int l=n+1,r=0;
        for (int i=1;i<=n;i++)
         if (a[i][j]==1) l=min(l,i),r=max(r,i);
        c[j].l=l; c[j].r=r;
        if (c[j].r<c[j].l) ret++;
    }
    int res=0; rec=ret;
    for (int i=1;i<=n-k+1;i++) {
        for (int j=1;j<=k;j++)
         if (c[j].l<=c[j].r && i<=c[j].l &&c[j].r<=i+k-1) ret++;
        res = max(res,ans[i][1]+ret);
        for (int j=2;j<=n-k+1;j++) {
            if (c[j-1].l<=c[j-1].r && i<=c[j-1].l && c[j-1].r<=i+k-1) ret--;
            if (c[j+k-1].l<=c[j+k-1].r && i<=c[j+k-1].l && c[j+k-1].r <= i+k-1) ret++;
            res = max(res , ans[i][j]+ret);
        }
        ret=rec;
    }
    printf("%d\n",res);
    return 0;
 }
 
D.cpp

Problem E Compress Words

可以使用双hash,然后题意不是两个相邻的单词合并,而是之前已经合并过的单词和下一个单词合并。

复杂度大概是$O(\sum |S_i|)$ ,其中$|S_i|$表示第$i$个单词的长度。

# include <cstdio>
# include <iostream>
# include <cstring> 
# define hash Hash
# define e1 E1
# define e2 E2
# define mo1 MO1
# define mo2 MO2
# define int long long
using namespace std;
const int N=2e6+10,e1=127,e2=331;
const int mo1=1e9+7,mo2=998244353;
int Pow1[N],Pow2[N];
string s1,s2;
int n;
struct StringHash{
    pair<int,int> hash[N];
    void build(string s) {
        int len=s.length(); hash[0]=make_pair(0,0); 
        for (int i=1;i<=len;i++)
            hash[i].first=(hash[i-1].first*e1%mo1+s[i-1])%mo1,
            hash[i].second=(hash[i-1].second*e2%mo2+s[i-1])%mo2;
    }
    pair<int,int> gethash(int l,int r) {
        pair<int,int>ans;
        ans.first=((hash[r].first-Pow1[r-l+1]*hash[l-1].first%mo1)%mo1+mo1)%mo1;
        ans.second=((hash[r].second-Pow2[r-l+1]*hash[l-1].second%mo2)%mo2+mo2)%mo2;
        return ans;
    }
}S,T;
void work()
{
    int len1=s1.length(),len2=s2.length();
    T.build(s2); int ans=0;
    for (int mid=1;mid<=min(len1,len2);mid++)
     if (S.gethash(len1-mid+1,len1) == T.gethash(1,mid)) ans=mid; 
    for (int i=ans;i<len2;i++) {
        S.hash[i-ans+1+len1].first=(S.hash[i-ans+len1].first*e1%mo1+s2[i])%mo1,
        S.hash[i-ans+1+len1].second=(S.hash[i-ans+len1].second*e2%mo2+s2[i])%mo2;
        s1+=s2[i];
    }
}
signed main() {
    cin.sync_with_stdio(0); cin.tie(0);
    Pow1[0]=Pow2[0]=1; 
    for (int i=1;i<=1000000;i++) Pow1[i]=Pow1[i-1]*e1%mo1;
    for (int i=1;i<=1000000;i++) Pow2[i]=Pow2[i-1]*e2%mo2;
    cin>>n; cin>>s1; S.build(s1);
    for (int i=2;i<=n;i++) {
        cin>>s2;  work();  
    }
    cout<<s1;
    return 0;
}
E.cpp

Problem F White Lines

考虑到每个点的出度最大只有$10$。所以对于到达任意点的权$k$来说,只有$lcm(1..10) = 2520$种不同可能的后果。

所以所有权值$k$只需要在$mod 2520$的意义下计算就行。

记$ans[u][val]$表示当前到达顶点$u$当前带权为$val$的答案。

那么可以用记忆化搜索来维护这一条路径,然后用set来去重,就可以得到答案了。

由于每个点每个状态只可能访问一次,所以本题时间复杂度是$O(lcm(1..10) \times n)$

# include <bits/stdc++.h>
using namespace std;
const int N=1001,M=2521;
vector<int>E[N];
int k[N],ans[N][M],n;
bool vis[N][M];
pair<int,int>pre[N][M];
int dfs(int u,int val)
{
    if (ans[u][val]) return ans[u][val];
    vis[u][val]=1;
    int nex=(val+k[u])%2520;
    int v=E[u][nex%E[u].size()];
    if (ans[v][nex]) return ans[u][val]=ans[v][nex];
    if (!vis[v][nex]) {
        pre[v][nex]=make_pair(u,val);
        return ans[u][val]=dfs(v,nex);
    } else {
        set<int>s;
        pair<int,int> now = make_pair(u,val);
        while (now!=make_pair(v,nex)) {
            s.insert(now.first);
            now=pre[now.first][now.second];
        }
        s.insert(now.first);
        return ans[u][val]=s.size();
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",&k[i]),k[i]=(k[i]%2520+2520)%2520;
    for (int i=1;i<=n;i++){
        int t; scanf("%d",&t);
        for (int j=1;j<=t;j++) {
            int v; scanf("%d",&v);
            E[i].push_back(v);
        }
    }
    int q; scanf("%d",&q);
    while (q--) {
        int x,y; scanf("%d%d",&x,&y);
        printf("%d\n",dfs(x,(y%2520+2520)%2520));
    }
    return 0;
}
F.cpp

 

posted @ 2019-08-12 21:09  ljc20020730  阅读(241)  评论(0编辑  收藏  举报