hgoi#20190509

T1-k-Factorization

给一个正整数n,找到k个整数(不必不同),这些整数都严格大于1,并且它们的乘积等于n。

解法

n这么小,直接暴力吧=-=
判断分解出来的质因数有没有到k个,没到就无解
输出前k-1个,后面的乘在一起,输出就好了

ac代码

#include<bits/stdc++.h>
using namespace std;
int n,k,cnt,ans[100];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=2;i<=n;i++)while(n%i==0)ans[++cnt]=i,n/=i;
    if(k>cnt){puts("-1");return 0;}
    for(int i=k+1;i<=cnt;i++)ans[k]*=ans[i];
    for(int i=1;i<=k;i++)printf("%d ",ans[i]);
    return 0;
}

T2-Odd sum

现给予你一个长度为n,包含正整数的序列 a1,a2……an,你的任务是找到一个和为奇数且值最大(在和为奇数的子序列中)的子序列。可以保证该序列中有和为奇数的子序列
子序列是一个可以通过删除一个序列中的部分元素,但不改变其他元素的顺序后得到的新序列
现在请你写一个程序找到上述的这样一个子序列

解法

因为要最大,就把所有正的加起来,有可能是偶数,怎么办呢,减去一个奇数就好了嘛,减哪个呢
预先找出绝对值最小的奇数,如果是正数,相当于减去它,如果是负数,相当于加上它
这样肯定是最优的(不需要证明了吧=-=)

ac代码

#include<bits/stdc++.h>
using namespace std;
int n,a[100010],sum,x=1e5;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(a[i]>0)sum+=a[i];
        if(abs(a[i])&1)x=min(x,abs(a[i]));
    }
    if(sum&1)printf("%d\n",sum);
    else printf("%d\n",sum-x);
    return 0;
}

T3-Minimal string

给出一个字符串,按照从前到后的顺序进栈,输出字典序最小的出栈序列

解法

因为要字典序最小,所以优先输出最小的比较好,每次设定一个小写字母,从a枚举到z
先找在当前位置之前小于等于它的,要优先于后面的出栈
然后将后面所有的该字母找出来,位置设定为最后的该字母
这样一定是最优的,很显然啊(大雾

ac代码

#include<bits/stdc++.h>
using namespace std;
char s[100010],st[100010],ch;
int del[100010],len,cnt,nw,last;
int main()
{
    scanf("%s",s),len=strlen(s);
    for(int i=1;i<=26;i++)
    {
        ch='a'+i-1,nw=last-1;
        while(nw>=0&&(s[nw]<=ch||del[nw]))
            {if(!del[nw])st[++cnt]=s[nw],del[nw]=1;nw--;}
        nw=last;
        while(nw<len)
        {
            if(s[nw]==ch)st[++cnt]=ch,del[nw]=1,last=nw;
            nw++;
        }
    }
    for(int i=len-1;i>=0;i--)if(!del[i])st[++cnt]=s[i];
    for(int i=1;i<=cnt;i++)printf("%c",st[i]);
    return 0;
}

T4-Broken BST

给一棵二叉搜索树,但是不保证这是一棵正确的二叉搜索树,那么按照二叉搜索树的搜索算法(小往左,大往右),可能找不到某些节点,你的任务是计算有多少节点将不会被遍历到

解法

这道题题意有点难理解。。。如果有相同的数字,只要有一个被遍历到了,其他都会被遍历到emm
如果暴力的去查询每一个节点的话,一条链就被卡成n方了emmm
所以换一种方法,因为二叉搜索树上每一个节点都有一个范围l<x<r,如果满足,这个点就扫得到
由于很坑的题意,用个map存一下,就差不多了

ac代码

#include<bits/stdc++.h>
using namespace std;
int n,rt,a[100010],ls[100010],rs[100010],vis[100010],ans;
map<int,bool>mp;
void f(int nw){if(nw==-1)return;vis[nw]=1;}
void dfs(int nw,int l,int r)
{
    if(nw==-1)return ;
    if(a[nw]>l&&a[nw]<r)mp[a[nw]]=1;
    dfs(ls[nw],l,min(r,a[nw])),dfs(rs[nw],max(l,a[nw]),r);
}
void get_ans(){for(int i=1;i<=n;i++)if(!mp[a[i]])ans++;}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d%d",&a[i],&ls[i],&rs[i]),f(ls[i]),f(rs[i]);
    for(int i=1;i<=n;i++)if(!vis[i])rt=i;
    dfs(rt,-1,1e9+1),get_ans(),printf("%d\n",ans);
    return 0;
}

T5-Array Queries

给定一个序列,有q次询问,每次询问给定p和k,重复执行操作p=p+a[p]+k,直到p大于n,询问的答案为操作次数

解法

一看数据范围,如果纯模拟是n方的,过不了
然后发现,过不了是因为k太小,所以把k小的打表预处理出来就ok了a.a
那这个小的设定在多少呢,sqrt(n)左右就可以了
总的复杂度就是n×sqrt(n)了

ac代码

#include<bits/stdc++.h>
#define lim 320
using namespace std;
int n,q,x,y,a[100010],dp[100010][400],ans;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=n;i>=1;i--)for(int j=1;j<=lim;j++)
    {
        dp[i][j]=1;
        if(i+a[i]+j<=n)dp[i][j]+=dp[i+a[i]+j][j];
    }
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d",&x,&y);
        if(y<=lim)printf("%d\n",dp[x][y]);
        else
        {
            while(x<=n)ans++,x+=a[x]+y;
            printf("%d\n",ans),ans=0;
        }
    }
    return 0;
}

T6-Mice and Holes

n个老鼠,m个洞,告诉你他们的一维坐标和m个洞的容量限制,问最小总距离。

解法

明显是dp,dp[i][j]表示前j只老鼠进了前i个洞的最小距离
无解很好判断,只要老鼠数量大于洞的总容量就无解
转移方程呢,dp[i][j]=min(dp[i-1][k]+sum[i][j]-sum[i][k])
sum[i][j]表示前j只老鼠都到i号洞的距离
发现这样做是n^3,接受不了,需要优化
因为sum[i][j]是定值,将dp[i-1][k]-sum[i][k]维护成一个单调队列,就可以n方做了

ac代码

#include<bits/stdc++.h>
#define inf 2333333333333333
#define ll long long
using namespace std;
struct node{ll x,s;}b[5010];
ll n,m,l,r,a[5010],s[5010],sum[5010],dp[5010][5010],q[5010];
ll cmp(node x,node y){return x.x<y.x;}
int main()
{
    scanf("%lld%lld",&n,&m),memset(dp,inf,sizeof(dp)),dp[0][0]=0;
    for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(ll i=1;i<=m;i++)scanf("%lld%lld",&b[i].x,&b[i].s);
    sort(a+1,a+1+n),sort(b+1,b+1+m,cmp);
    for(ll i=1;i<=m;i++)s[i]=s[i-1]+b[i].s;
    if(s[m]<n){puts("-1");return 0;}
    for(ll i=1;i<=m;i++)
    {
        dp[i][0]=l=r=0,q[++r]=0;
        for(ll j=1;j<=s[i]&&j<=n;j++)
        {
            dp[i][j]=dp[i-1][j],sum[j]=sum[j-1]+abs(a[j]-b[i].x);
            while((j-q[l]>b[i].s)||l<=r&&dp[i-1][q[l]]-sum[q[l]]>dp[i-1][j]-sum[j])l++;
            q[++r]=j,dp[i][j]=min(dp[i][j],sum[j]+dp[i-1][q[l]]-sum[q[l]]);
        }
    }
    printf("%lld\n",dp[m][n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值