BestCoder Round #83

A题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5680

官方题解:

通过观察样例猜测答案即\max(a_i)max(ai),可以对小规模的数据进行模拟验证,实际答案就是\max(a_i)max(ai),时间复杂度O(n)O(n)

由于a_iai互不相同,因此可以考虑每个元素在哪些子集中是最小值。

a_iaiAA中第kk大的元素,以a_iai为最小值的集合只能包含不小于a_iai的元素,因此这样的集合有2^{k-1}2k1个。

如果k=1k=1,则只有{a_i}{ai}a_iai为最小值;否则,上述集合中包含最大元素的子集和不包含最大元素的子集的元素个数奇偶性不同,对答案的贡献相互抵消。因此有S_{odd}-S_{even}=\max(a_i)SoddSeven=max(ai)

实际上,a_iai不需要互不相同,也可以得到答案是\max(a_i)max(ai)的结论。

我的思考:

之前想的没有观察答案,想的实在是很复杂,用了组合数,傻傻的去算,还一直在担心极端情况会不会爆ll,但也算是a了,学了下组合数递推的写法

#include<cstdio>//官方题解
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
    int T,a,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int ans=0;
        while(n--)
        {
            scanf("%d",&a);
            ans=max(ans,a);
        }
        printf("%d\n",ans);
    }
    return 0;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll __int64
using namespace std;
ll a[40],c[31][31];
void init()
{
    c[0][0]=1;
    for(int i=1;i<=30;i++)
    {
        c[i][0]=1;
        for(int j=0;j<=i;j++)
            c[i][j]=c[i-1][j]+c[i-1][j-1];
    }
}
int main()
{//freopen("C:\\Users\\Administrator\\Desktop\\input.txt","r",stdin);
    int T,n;
    init();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        ll ans=0;
        for(int i=0;i<n;i++)
            scanf("%I64d",&a[i]);
        sort(a,a+n);
        for(int i=0;i<n;i++)
        {
            int tmp=n-i-1;
            int tmp1=2,tmp2=1;
            while(tmp>=tmp1)
            {
                ans+=c[tmp][tmp1]*a[i];
                ans-=c[tmp][tmp2]*a[i];
                tmp1+=2;
                tmp2+=2;
            }
            if(tmp>=tmp2)ans-=c[tmp][tmp2]*a[i];
        }
        for(int i=0;i<n;i++)
            ans+=a[i];
        printf("%I64d\n",ans);
    }
    return 0;
}

B题  链接:http://acm.hdu.edu.cn/showproblem.php?pid=5681

官方题解:

通过二分在O(n\log n)O(nlogn)的时间复杂度下将路由器能覆盖的区间[l_i,r_i][li,ri]求出来,并令f[i][j]f[i][j]表示用ii个路由器覆盖前jj个点的最小代价。

对于第一种方案,选取jj点设置路由器可以得到的转移是f[i][r_j]\leftarrow\min_{l_j-1\leq k\leq r_j}{f[i-1][k]} + a_if[i][rj]minlj1krjf[i1][k]+ai

对于第二种方案,选取jj点设置光缆可以得到的转移是f[i][j]\leftarrow f[i][j-1]+b_if[i][j]f[i][j1]+bi

因此可以利用树状数组维护后缀区间最大值做到离线O(nk\log n)O(nklogn)

注意到选择互相包含的区间是不会成为最优解的,因此可以将第一组转移改为f[i][r_j]\leftarrow\min_{l_j-1\leq k}{f[i-1][k]} + a_if[i][rj]minlj1kf[i1][k]+ai,即可做到O(nk)O(nk)

我的思考:

比赛的时候写了下网络流,写完了发现没看见k个路由器的限制,正解是dp,类似邻接表的写法维护左右区间,学到了

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=20005;
int T,n,m,i,j,k,a[N],b[N],r[N],d[N],f[105][N],h[N],ne[N],p[N];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(h,0,sizeof(h));
        scanf("%d%d",&n,&m);
        for(i=2;i<=n;i++)
        {
            scanf("%d",&d[i]);
            d[i]+=d[i-1];
        }
        d[n+1]=0x7fffffff;//处理后d[i]表示i节点和1节点的距离
        for(i=1;i<=n;i++)
        {
            scanf("%d%d%d",a+i,r+i,b+i);
            j=lower_bound(d+1,d+n+1,d[i]-r[i])-d;//左端点
            k=upper_bound(d+1,d+n+1,d[i]+r[i])-d-1;//右端点
            p[i]=j;//设置i的左端点为j
            ne[i]=h[k];//ne[i]记录的是前一个以k为右端点的节点id
            h[k]=i;//设置最近一个以k为右端点的节点id为i
        }//和上一条语句一起记录了所有以k为右端点的节点集
        for(i=1;i<=n;i++) f[0][i]=f[0][i-1]+b[i];//0个路由器的情况f[n][m]记录有n个路由器前m个节点网络连通的最小cost
        for(int i=1;i<=m;i++)//枚举路由器个数
        {
            for(j=1;j<=n;j++)//枚举网络连通的节点个数
                for(k=h[j],f[i][j]=f[i][j-1]+b[j];k;k=ne[k])//枚举所有以j为右端点的节点更新f[i][j]
                    f[i][j]=min(f[i][j],f[i-1][p[k]-1]+a[k]);
            for(j=n-1;j;j--) f[i][j]=min(f[i][j],f[i][j+1]);//不以j为右端点的同样为i个路由器中可能会有更小的cost值包含了超过j个节点
        }
        printf("%d\n",f[m][n]);
    }
    return 0;
}

C题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5682

官方题解:

二分答案LL,检查是否存在答案不超过LL的解,也即对于任意一条边上的两个点uuvv,有|level_u-level_v|\leq LlevelulevelvL,如果能维护出每个点可能的取值区间就可以判断是否有解了。

对于n>1n>1的情况,随便找点做根,做树形dp即可,总的时间复杂度为O(n\log\max(w))O(nlogmax(w))

觉得dfs容易爆栈的话可以设一个虚点做起点,连向已经确定levellevel的叶子,通过bfs确定一个拓扑序,在序上更新即可,时间复杂度同上。

Claris说随便选个点bfs就可以了。

这个题可以扩展,确定levellevel的点可以不是叶子,扩展版本用虚树做也是十分容易的。

我的思考:二分答案+树形dp

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=50005;
int T,n,k,root,flag;
bool vis[N];
int id[N],w[N],head[N],tp;
struct Edge
{
    int v,next;
}edge[N<<1];
void ae(int u,int v)
{
    edge[tp].v=v;
    edge[tp].next=head[u];
    head[u]=tp++;
}
pair<int,int> dfs(int u,int f,int mid)
{
    int l=0,r=1e9,v;
    if(vis[u]) return make_pair(max(1,w[u]-mid),min(1000000000,w[u]+mid));
    for(int i=head[u];i!=-1&&flag;i=edge[i].next)//遍历u的所有边
    {
        if((v=edge[i].v)!=f)//如果v不是u的前一个点防止重复遍历
        {
            pair<int,int> tmp=dfs(v,u,mid);//求v的区间范围
            if(tmp.first>r||tmp.second<l) {flag=0;break;}//如果范围不在已知范围内跳出
            l=max(l,tmp.first);
            r=min(r,tmp.second);
        }
    }
    return make_pair(max(1,l-mid),min(1000000000,r+mid));
}
bool check(int x)
{
    flag=1;
    dfs(root,0,x);
    return flag!=0;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        memset(head,-1,sizeof(head));tp=0;
        for(int u,v,i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            ae(u,v);ae(v,u);
        }
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=k;i++)
        {
            scanf("%d",&id[i]);
            scanf("%d",&w[id[i]]);
            vis[id[i]]=1;
        }
        if(n==2)
        {
            if(k==2) printf("%d\n",max(w[1]-w[2],w[2]-w[1]));
            else printf("%d\n",0);
            continue;
        }
        for(int i=1;i<=n;i++)
        {
            if(!vis[i])
            {
                root=i;
                break;
            }
        }
        int l=0,r=1000000000,mid,ans=0;//二分答案
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
D题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5683

官方题解:

由于异或运算的特性,答案可以按位考虑,只需要维护出当前状态有多少对(a_i+a_j)(ai+aj)的第kk位是11,做O(\log\max(a_i))O(logmax(ai))次即可。

(a_i+a_j)(ai+aj)的第kk位是11,即((a_i+a_j)\;mod\;2^{k+1})\geq2^k((ai+aj)mod2k+1)2k,而0\leq(a_i\;mod\;2^{k+1})+(a_j\;mod\;2^{k+1}) < 2^{k+2}0(aimod2k+1)+(ajmod2k+1)<2k+2,所以若令b_i=(a_i\;mod\;2^{k+1})bi=(aimod2k+1),则对于给定的ii,满足条件的jj2^k-b_i\leq b_j<2^{k+1}-b_i2kbibj<2k+1bi或者b_j\geq 2^{k+1} + 2^k - b_ibj2k+1+2kbi

利用离散型权值线段树可以很简便地维护题目中的修改和查询,总时间复杂度O(n\log n\log\max(a_i))O(nlognlogmax(ai))

我的思考:

只能说暴力出奇迹,纯暴力A

#include<cstdio>
#include<cstring>
using namespace std;
const int N=20005;
int a[N];
int main()
{
    int T,n,m,x,y,sum;
    scanf("%d",&T);
    while(T--)
    {
        sum=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                sum^=(a[i]+a[j]);
            }
        }
        while(m--)
        {
            scanf("%d%d",&x,&y);
            for(int i=1;i<=n;i++)
            {
                if(i!=x)
                {
                    sum^=(a[i]+a[x]);
                    sum^=(a[i]+y);
                }
            }
            printf("%d\n",sum);
            a[x]=y;
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值