2020牛客暑期多校训练营(第五场)BDEFI

101北林一队 北京林业大学511:20:10

F:模拟即可,记得开long long

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
 
ll a[110];
int main()
{
    ll n;
    scanf("%lld",&n);
    ll mx=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        mx=max(mx,a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        ll tp=a[i],up=(ll)50*a[i]/mx;
        if(a[i]*50%mx!=0)up++;
    
        printf("+");for(int j=1;j<=up;j++)printf("-");printf("+");puts("");
        printf("|");for(int j=1;j<up;j++)printf(" ");
        if(a[i]>0)
        {
            if(tp==mx)printf("*");else printf(" ");
        }
        printf("|");printf("%d",tp);puts("");
        printf("+");for(int j=1;j<=up;j++)printf("-");printf("+");
        puts("");
    }
    return 0;
}

I:

GHHEHHGHHE

HHGHHEHHGH

HEHHGHHEHH

GHHEHHGHHE

2/3

#include<bits/stdc++.h>
using namespace std;
 
int main(){
    puts("0.666667");
}

E:置换群最小循环节,水题,不过要JAVA大数

    import java.math.BigInteger;
    import java.util.Scanner;
 
    public class Main {
 
        static int n,len;
        static int[] p = new int[200005];
        static int[] vis = new int[200005];
        static int[] cnt = new int[200005];
 
        public static void main(String[] args) {
            Scanner sin = new Scanner(System.in);
            n=sin.nextInt();
            for(int i=1;i<=n;i++){
                vis[i]=cnt[i]=0;
                p[i]=sin.nextInt();
            }
 
            /*n=50000;
            for(int i=1;i<n;i++)p[i]=i+1;
            p[n]=1;
*/
            for(int i=1;i<=n;i++)
                if(vis[i]==0){
                    ++len;
                    cnt[len]++;
                    vis[i]=1;
                    int now=i;
                    while(vis[p[now]]==0){
                        now=p[now];
                        cnt[len]++;
                        vis[now]=1;
                    }
                }
            //for(int i=1;i<=n;i++)
                //System.out.println(cnt[i]);
 
            StringBuilder smod = new StringBuilder("1");
            for(int i=1;i<=n;i++)
                smod.append("0");
            BigInteger mod = new BigInteger(smod.toString());
            BigInteger ans = new BigInteger("1");
            for(int i=1;i<=len;i++){
                BigInteger now = new BigInteger(String.valueOf(cnt[i]));
                BigInteger gcd = ans.gcd(now);
                ans=ans.multiply(now).divide(gcd);
                if(ans.compareTo(mod)>0)
                    ans.remainder(mod);
            }
 
            System.out.println(ans);
        }
    }

D:

2操作等价于把链变成环(2操作可以无限次)

1操作等价于把一个数往前移动任意位置。

所以最后结果就是环上的LIS。

for一遍Onlogn求LIS即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 500+7;
/*
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
int a[M];
int b[M],d[M];
int n;
int LIS()
{
    memset(d,0,sizeof(d));
    int l,r,mid,len=1;
    d[1]=a[1];
    for(int i=1;i<=n;i++)
    {
        l=1,r=len;
        if(a[i]>d[len])
        {
            d[++len]=a[i];
            continue;
        }
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(d[mid]<a[i])
                l=mid+1;
            else r=mid-1;
        }
        d[l]=a[i];
        if(l>len)len++;
    }
    return len;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    int mn=n+7;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            int tp=(j+i)%n;if(tp==0)tp=n;
            a[j]=b[tp];
        }
        //for(int j=1;j<=n;j++)cout<<a[j]<<" ";
    //  cout<<endl;
        mn=min(mn,n-LIS());
    }
    printf("%d\n",mn);
    return 0;
}

B:

最后结果肯定是MST。因为任意两点间的连边值是固定的,x-y的异或和为:f[1,x]^f[1,y]。

但这是稠密图的MST。连边很特殊。很容易想到Boruvka算法 求MST(没学过的可以看我之前写的博客)

但这个算法的瓶颈在于如何快速找到一个点到其他连通快点异或最小边权。

我们仔细观察条件,按位异或,自然的想到从高位开始。

把最高位为0的和为1的分成2组,这两组之间连一条权值最小的边,其他内部连,一定是最优解。。(根据Boruvka的思想)

因为我们目的是一直让不同连通快点连边,边权尽量小,把当前最高位01分组后,01之间必须要至少连一条边(否则最后不连通),然后0内部相连 ,1内部相连,变成两个子问题。

上述操作直接分治处理即可。(把f数组排序,每一位01一定是在两段区间的)

可以看看我之前写的这道题,很类似,只不过这题把点权改成了边权,本质是一样的。

https://blog.csdn.net/bjfu170203101/article/details/104321068

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 2e5+7;
 
int head[M],CNT=1;
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++CNT].nxt=head[x],ee[CNT].w=w,ee[CNT].to=y,head[x]=CNT;}
 
int a[M];
int ti[M*32][2],cnt;//字典树
ll ans;
void in(int x)
{
    int k=0;
    for(int i=29;i>=0;i--)
    {
        int id=(x>>i)&1;
        if(ti[k][id]==0)
        {
            ti[k][id]=++cnt;
            k=cnt;ti[k][0]=ti[k][1]=0;
        }
        else k=ti[k][id];
    }
}
int fd(int x)//找到字典树中存的树与x异或最小
{
    int k=0,ans=0;
    for(int i=29;i>=0;i--)
    {
        int id=(x>>i)&1;
        if(ti[k][id]==0)id^=1;
        k=ti[k][id];
        if(id)ans|=(1<<i);
    }
    return ans;
}
void dfs(int id,int l,int r)
{
     
    if(id<0)return ;
    int mid=l-1;
    for(int i=l;i<=r;i++)
        if(((a[i]>>id)&1)==0)
            mid=i;
    if(l<=mid)dfs(id-1,l,mid);
    if(mid+1<=r)dfs(id-1,mid+1,r);
    if(l<=mid&&mid+1<=r)//这一步放在dfs前和dfs后都行
    {
        cnt=0;
        ti[0][0]=ti[0][1]=0;
        for(int i=l;i<=mid;i++)in(a[i]);
        int mi=2e9;
        for(int i=mid+1;i<=r;i++)
            mi=min(mi,a[i]^fd(a[i]));
        ans+=mi;
    }
}
void dfs(int x,int fat)
{
    for(int i=head[x];i;i=ee[i].nxt)
    {
        int y=ee[i].to,w=ee[i].w;
        if(y==fat)continue;
        a[y]=a[x]^w;
        dfs(y,x);
    }
}
int main()
{
    int n,u,v,w;
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++)scanf("%d%d%d",&u,&v,&w),u++,v++,add(u,v,w),add(v,u,w);
    dfs(1,0);
//  for(int i=1;i<=n;i++)cout<<a[i]<<endl;
    sort(a+1,a+1+n);
    dfs(29,1,n);
    printf("%lld\n",ans);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值