Codeforces Round #476 (Div. 2) [Thanks, Telegram!]

A.纯粹签到
B.
用两个前缀和维护一下每个位置的连续的可放位置的累计数(横向与竖直方向),然后枚举每个位置即可。

#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int cnth[105][105],cntl[105][105],sum[105][105];
int main()
{
    int n,k,i,j;string str;
    cin>>n>>k;getchar();
    for(i=1;i<=n;i++){
        getline(cin,str);
        for(j=0;j<n;j++){
            if(str[j]=='#')continue;//如果该位置不可放,前缀和两个都为0 
            cnth[i][j+1]=cntl[i][j+1]=1;
            cnth[i][j+1]+=cnth[i][j];
            cntl[i][j+1]+=cntl[i-1][j+1];
        }
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=n;j++){
            if(j-k+1>=1){
                if(cnth[i][j-k+1]&&cnth[i][j]-cnth[i][j-k+1]+1==k){//如果横向累计已经可放
                    for(int p=j-k+1;p<=j;p++)
                        sum[i][p]++;
                }
            }
            if(i-k+1>=1){
                if(cntl[i-k+1][j]&&cntl[i][j]-cntl[i-k+1][j]+1==k){//竖直方向累计可放
                    for(int p=i-k+1;p<=i;p++)
                        sum[p][j]++;
                }
            }
        }
    }
    int maxn=0,ai=1,aj=1;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
        if(maxn<sum[i][j]){
            maxn=sum[i][j];ai=i;aj=j;
        }
    }
    cout<<ai<<' '<<aj<<endl;
    //cout<<sum[1][8]<<' '<<sum[12][8];

    return 0;
}

C.
题目数据范围很大,但是仔细观察可以发现d的范围却很小,只有1k,所以可以枚举d来解决问题。注意到d只用枚举到d-1即可,因为对于枚举的每个d,我们只要保证Arkady在d+1轮中也能得到糖就行。如果d+1轮中所有人都恰好分到了糖,那么就是刚好分了d轮,否则就是分了d-1轮。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main()
{
    ll n,k,m,d,i,j,maxn=0;
    cin>>n>>k>>m>>d;
    for(i=0;i<d&&k*i+1<=n;i++){
        maxn=max(maxn,(i+1)*min(m,(n/(k*i+1))));
    }
    cout<<maxn<<endl;

    return 0;
}

D.
这道题首先很显然可以用最大流来搞,把每个石头一拆为二,连一条容量为1的边来保证每个石头只被使用一次,然后对于每个石头,向他能够够到的范围内的后面的石头连边就好了,最后跑一遍最大流即可。然而,由于边数过多,因此朴素的加边会导致tle。根据最大流最小割定理,我们会发现其实我们只要找出这幅图中的最小割即可。注意到这幅图的特殊性:对于任意连续的k个石头,前面的石头流出的流量必然流入这k个石头,然后所有后面的石头的流量也必然都来自这k个石头。因此我们只需要从前往后扫一遍,找出所有的连续k个石头中最小的石头数就是答案。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int a[100005];
int main()
{
    int w,l,i,j,k;
    cin>>w>>l;int minn=0x7f7f7f7f,s=0;
    for(i=1;i<w;i++){
        scanf("%d",&a[i]);s+=a[i];s-=i>l?a[i-l]:0;minn=i>=l?min(minn,s):minn;
    }
    cout<<minn<<endl;

    return 0;
}

E.
首先我们根据所给单词构造一棵trie树,然后我们的目标很简单:在不重复的前提下使得所有单词尽可能的等于它的最短的前缀。那么我们可以从顶点开始dfs,对于每一个节点,如果当前节点并不是某个单词的结尾,那么我们就可以把它的所有子节点中最长的那个单词变成以当前节点为结尾的单词,这样一定是最优的。对于每个搜索,我们返回一个从大到小排序的multiset即可。

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
using namespace std;
struct node{
    char c;
    vector<node*>v;
    bool tag;
    node(){
        v.clear();tag=false;
    }
}*root=new node();
struct cmp{
    bool operator()(int a,int b){
        return a>b;
    }
};
typedef multiset<int,cmp>S;
void add(string str)
{
    node*p=root,*q;
    int i,j,k;
    for(i=0;i<str.size();i++){
        for(j=0;j<p->v.size();j++){
            if(p->v[j]->c==str[i])
                break;
        }
        if(j==p->v.size()){
            q=new node();q->c=str[i];p->v.push_back(q);p=q;
        }
        else{
            p=p->v[j];
        }
    }
    p->tag=true;
}
S dfs(node*p,int depth)
{
    S s,s1;int i,j;
    for(i=0;i<p->v.size();i++){
        s1=dfs(p->v[i],depth+1);
        s.insert(s1.begin(),s1.end());
    }
    if(p!=root) {
        if (p->tag)s.insert(depth);
        else {
            if (!s.empty()) {
                s.erase(s.begin());
                s.insert(depth);
            }
        }
    }
    return s;
}
int main()
{
    int n,i,j;string str;
    cin>>n;getchar();
    for(i=1;i<=n;i++){
        getline(cin,str);
        add(str);
    }
    S s=dfs(root,0);
    int ans=0;
    for(auto a:s)
        ans+=a;
    cout<<ans<<endl;
    //cout<<s.size()<<endl;

    return 0;

}

猜你喜欢

转载自blog.csdn.net/humveea6/article/details/80243536