Zut_round 5 (CF #506 Div.3)

CF #506 DIV3

A:

题意:求匹配的最长前缀和后缀

思路:kmp求next数组或直接枚举前缀结束的位置即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MX = 1e6+5;

int main(){
    int n,k;cin>>n>>k;
    string t;cin>>t;int lt=t.length();
    int u=-1;
    for(int i=0;i<lt-1;i++){
        if(t[i]==t[lt-1]){
            int p1=i,p2=lt-1,tag=1;
            for(int j=p1;j>=0;j--){
                if(t[j]!=t[p2--]) {tag=0;break;}
            }
            if(tag) u=i;
        }
    }
    string pre=t.substr(0,u+1);
    string suf=t.substr(u+1,lt);
    cout<<pre;
    while(k--)cout<<suf;
    return 0;
}

B

题意:给递增数组a,求最长的子序列,满足每项小于等于前一项的两倍。

思路:显然最长的子序列一定是数组a的子串(也就是连续),那么从头到尾扫一遍即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MX = 1e6+5;
int a[MX];

int main(){
    int n;cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    int  ans=1,t=1;
    for(int i=2;i<=n;i++){
        if(a[i]>a[i-1]*2) ans=max(ans,t),t=1;
        else t++;
    }
    cout<<max(ans,t)<<endl;
    return 0;
}

C

题意:给n个区间,求拿走一个区间之后,所有区间的交最大的区间长度。

思路1:影响答案最大的显然是L最大的和R最小的,那么比较删这两种哪种最后得到的区间长度大。

思路2:

思路1,我的代码

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
const int MX = 1e6+5;
 int n;
pair<int,int>p[MX];

int del(int u){
    int l=INT_MIN,r=INT_MAX;
    for(int i=1;i<=n;i++){
        if(i==u)continue;
        l=max(l,p[i].FI);
        r=min(r,p[i].SE);
    }
    return max(0,r-l);
}
int main(){
   cin>>n;
    int mi=INT_MAX,mx=INT_MIN,ui,ux;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&p[i].FI,&p[i].SE);
        if(p[i].FI>mx) {
            mx=p[i].FI,ux=i;
        }
        if(p[i].SE<mi){
            mi=p[i].SE,ui=i;
        }
    }
    cout<<max(del(ux),del(ui));
    return 0;
}

 D

题意:给n个数字,求能挑出几个不一样的二元对,使得二元对拼接起来后得到的数字x,使x%k==0

思路:枚举第一个数字ai,那么第二个数字可以枚举它的位数wi(最多10位),设第二个数字为aj,那么可得结论aj%k==(k-ai*10^wi)%k,

然后用map离散化预处理每个数%k即可。(1e9是10位数啊!!! )记得统计答案的时候,小心map自动构造无用数据...

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
const int MX = 1e6+5;
int a[MX],le[MX];
int n,k;
map<int,int>mp[11];

int getwei(LL x){
    int wei=0;
    while(x){wei++,x/=10;}
    return wei;
}
int main(){
    //cout<<getwei(LLONG_MAX)<<' '<<getwei(INT_MAX)<<' '<<getwei(1e9);
    LL ten_pow[20];
    ten_pow[0]=1;

    cin>>n>>k;
    for(int i=1;i<=11;i++) ten_pow[i]=ten_pow[i-1]*10LL%k;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);le[i]=getwei(a[i]);
        mp[le[i]][a[i]%k]++;
    }
    LL ans=0;
    for(int i=1;i<=n;i++){
        mp[le[i]][a[i]%k]--;
        for(int j=1;j<=10;j++){
            LL now=k-(LL)a[i]*(ten_pow[j])%k;
            auto it=mp[j].find((now==k?0:now));
			if(it!=mp[j].end())ans+=(LL)it->second;
        }
        mp[le[i]][a[i]%k]++;
    }
    cout<<ans;
    return 0;
}

E

题意:给一个树,边权都是1,求至少添加几条边,能使得顶点1到别的点的最短路长度小于等于2

https://codeforc.es/blog/entry/61439(官方题解

(我的复杂树形dp)思路:定义dp[][2][2] ,1号点是根节点,显然都应该和根节点连边是可行的

dp[i][0][0]:i点和i点的父亲节点都不和根节点连边状态下,i点这颗子树(包括i)需要的最小代价。

dp[i][1][1]:i点和i点的父亲节点都和根节点连边状态下,i点这颗子树(包括i)需要的最小代价。

dp[i][0][1]:i点和根节点连边&&i点的父亲节点不和根节点连边状态下,i点这颗子树(包括i)需要的最小代价。

dp[i][1][0]:i点不和根节点连边&&i点的父亲节点和根节点连边状态下,i点这颗子树(包括i)需要的最小代价。

注意叶子节点,和第三层的叶子节点。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
const int MX = 2e5+5;
vector<int>g[MX];
int de[MX],fa[MX];
int dp[MX][2][2];
void getde(int now,int pre,int d){//得到深度和父亲节点
    de[now]=d;fa[now]=pre;
    for(auto it:g[now]){
        if(it==pre) continue;
        getde(it,now,d+1);
    }
}
int dfs(int now,int pre,int cp,int cn){ //当前节点,父亲节点,父亲节点连边否,当前节点连边否
    if(dp[now][cp][cn]!=-1)return dp[now][cp][cn]; //记忆化搜索
    if(cn){  //如果当前节点连边,儿子节点可以连或不连
        int cnt=0;
        for(int it:g[now]){
            if(it==pre)continue;
            cnt=cnt+min(dfs(it,now,1,1)+1,dfs(it,now,1,0));
        }
        return dp[now][cp][cn]=cnt;
    }
    if(cp){ //如果当前节点不连边,父亲节点连边,儿子节点可以连或不连(当前儿子节点有节点)
        int cnt=0;
        for(int it:g[now]){
            if(it==pre)continue;
            if(g[it].size()>1)
                cnt=cnt+min(dfs(it,now,0,1)+1,dfs(it,now,0,0));
            else
                cnt=cnt+dfs(it,now,0,1)+1;
        }
        return dp[now][cp][cn]=cnt;
    }
    int tag=0;    //如果都不连边,儿子节点必须要有一个连边
    for(int it:g[now]){
        if(it==pre)continue;
        if(g[it].size()==1) {tag=1;break;}
    }
    if(tag==1||de[now]==3){
        int cnt=0;
        for(int it:g[now]){
            if(it==pre)continue;
            if(g[it].size()==1) cnt+=dfs(it,now,0,1)+1;
            else cnt=cnt+min(dfs(it,now,0,1)+1,dfs(it,now,0,0));
        }
        return dp[now][cp][cn]=cnt;
    }
    else {
        int cnt=0;int mi=INT_MAX;
        for(int it:g[now]){
            if(it==pre)continue;
            cnt=cnt+min(dfs(it,now,0,1)+1,dfs(it,now,0,0));
            mi=min(mi,dfs(it,now,0,1)+1-dfs(it,now,0,0));
        }
        if(mi>0) return dp[now][cp][cn]=cnt+mi;
        return dp[now][cp][cn]=cnt;
    }
}
int main(){
    int n;cin>>n;
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        g[u].push_back(v),g[v].push_back(u);
    }
    memset(dp,-1,sizeof(dp));
    getde(1,0,1);
    int ans=0;
    for(int i=1;i<=n;i++){
        if(de[i]==3){
            ans=ans+min(dfs(i,fa[i],0,0),dfs(i,fa[i],0,1)+1);
        }
    }
    cout<<ans;
    return 0;
}

题意:有a个blue正方块,b个red正方块,求a+b个方块拼成一个矩形s,且矩形内有一块颜色一样的矩形(面积为a或者b)。

思路:先预处理出来,a的因子,b的因子,c的因子。

枚举大矩形s的长度(chang),然后可以算出s的宽(kuan),那么判断可以满足a或者b是否可以是小矩形,假设a是组成的小矩形,那么a的宽度大于(Sa/kuan),那么二分找到这个宽度,判断是否合理即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
const int MX = 1e6+5;
vector<LL>va,vs,vb;
int main(){
    LL a,b;cin>>a>>b;
    LL sum=a+b;
    for(LL i=1;i*i<=sum;i++)if(sum%i==0)vs.push_back(i),vs.push_back(sum/i);
    for(LL i=1;i*i<=a;i++)if(a%i==0)va.push_back(i),va.push_back(a/i);
    for(LL i=1;i*i<=b;i++)if(b%i==0)vb.push_back(i),vb.push_back(b/i);
    sort(vs.begin(),vs.end());vs.erase(unique(vs.begin(),vs.end()),vs.end());
    sort(va.begin(),va.end());va.erase(unique(va.begin(),va.end()),va.end());
    sort(vb.begin(),vb.end());vb.erase(unique(vb.begin(),vb.end()),vb.end());
    LL ans=LLONG_MAX;
    for(LL it:vs){
        LL gao=sum/it;
        LL chang=a/gao+(a%gao!=0);
        auto id1=lower_bound(va.begin(),va.end(),chang);
        if(id1!=va.end()&&(*id1)<=it) {ans=min(ans,gao*2LL+it*2LL);continue;}
        chang=b/gao+(b%gao!=0);
        auto id2=lower_bound(vb.begin(),vb.end(),chang);
        if(id2!=vb.end()&&(*id2)<=it) {ans=min(ans,gao*2LL+it*2LL);}
    }
    cout<<ans;
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值