多校一总结

前面因为没有在场上A题,赛后A后也渐渐地没写,今天来写一发,重温一下可爱的多校


Hdu 4602 Partition  1003

题意:对整数n进行无序拆分,求数k在拆分中出现了多少次

正解:把整数n一个无序拆分可以看成把n个点(每个点代表1)排成一列,在点与点之间插若干个空,数k就相当于k个连续的点。
分两种情况考虑:
1) n=k+......或者n=......+k,那么剩余的(n-k-1)位置可以插或者不插空,即2×2^(n-k-1)种方法
2) n=...+k+...,那么k可以选择(n-k-1)个位置,剩余的(n-k-2)位置可以选择插空,即(n-k-1)*2^(n-k-2)种方法
一共,(n-k+3)*2^(n-k-2)种方法。
要注意n<k的情况。

很神奇的题目,羡慕那些数学好的人!

代码:

#include <iostream>//1003 Partition
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include<cmath>
#define INF 0x7fffffff
const long long M=1e9+7;
using namespace std;
long long  powm(long long  a,long long  b,long long c)
{
    long long ans=c,tmp=a;
    while(b)
    {
        if(b%2)
        {
            ans=(ans*tmp)%M;
        }
        tmp=(tmp*tmp)%M;
        b/=2;
    }
    return ans;
}
int main()
{
    int t;
    long long n,k;
    scanf("%d",&t);
    while(t--)
    {
        cin>>n>>k;
        if(n<k)printf("0\n");
        else if(n==k)printf("1\n");
        else
        {
            if(n-k-2==-1)
                cout<<(n-k+3/2)<<endl;
            else
            {
                long long  ss=powm(2,n-k-2,n-k+3);
                cout<<ss<<endl;
            }

        }
    }
    return 0;
}


Hdu 4604 Deque     1005

题意:按序给你一些数,每个数你可以加入双端队列(初始为空),在任何时候都你可以弹出元素。求最后能保留在双端队列中的最长不下降子序列长度。

思路:实际就是求从某个点开始,的最长不降子序列和最长不升子序列之和 减去两个中重复的数字

每拿到一个点a[i],就求在它为起点往左走的最长不上升子序列,在它的右边求最长不下降子序列,自己用手模拟时才瞬间明白

每次找分割点a[i]最好从后往前找,我试了一下,从前往后找很麻烦。。PS:从后往前求最长不下降升子序列,从前往后求最长不上升子序列

代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <map>
#define M 20000006
using namespace std;
int up[100005];//上升序列
int down[100005];//下降序列
int nump[100005];//记录上升序列中相同数字出现的个数
int numd[100005];
int a[100005];
int getup(int x,int y,int v)//往后找相当于下降
{
    int l=x,r=y,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(up[mid]>=v)l=mid+1;
        else
            r=mid-1;
    }
    return l;
}
int getdown(int x,int y,int v)
{
    int l=x,r=y,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(down[mid]<=v)l=mid+1;
        else
            r=mid-1;
    }
    return l;
}
int main()
{
    int t,n,i,j;
    scanf("%d",&t);
    while(t--)
    {
        memset(nump,0,sizeof(nump));
        memset(numd,0,sizeof(numd));
        scanf("%d",&n);
        for(i=1; i<=n; i++)
            scanf("%d",&a[i]);
        up[1]=a[n];
        nump[1]=1;
        down[1]=a[n];
        numd[1]=1;
        int s1=1,s2=1,mmax=1;
        for(i=n-1; i>=1; i--)
        {
            int d1=getup(1,s1,a[i]);
            if(d1>s1)s1++;
            up[d1]=a[i];
            nump[d1]=(d1>1 && up[d1-1]==a[i])? nump[d1-1]+1 : 1;

            int d2=getdown(1,s2,a[i]);
            if(d2>s2)s2++;
            down[d2]=a[i];
            numd[d2]=(d2>1 && down[d2-1]==a[i])?numd[d2-1]+1 : 1;

            int t=d1+d2-min(nump[d1],numd[d2]);
            if(t > mmax)
                mmax=t;
        }
        printf("%d\n",mmax);
    }
    return 0;

}



Hdu 4607 Park Visit   1008

题意:一棵树,问从任意点出发,访问k个点走过的最少的边数。

思路:首先找一条树上最长链,当k大于链上的点数时,就要走一些链的节点的子树且走回链上,也就是子树的边走了两次。

代码:

#include <iostream>//1008
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include<cmath>
#define INF 0x7fffffff
#define M 1000000007
using namespace std;
vector<int>p[100005];
int vis[100005];
int dp[100005];
int t,n,m,mmax;
void Bfs(int x)
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
    dp[i]=INF;
    queue<int>q;
    q.push(x);
    vis[x]=1;dp[x]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        int size=p[u].size();
        for(int i=0;i<size;i++)
        {
            int v=p[u][i];
            if(dp[u]+1<dp[v])
            dp[v]=dp[u]+1;
            if(vis[v]==0)
            {
                q.push(v);
                vis[v]=1;
            }
        }
    }
}
int main()
{
    scanf("%d",&t);
    int u,v,k;
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0; i<=n; i++)
            p[i].clear();
        for(int i=1; i<=n-1; i++)
        {
            scanf("%d%d",&u,&v);
            p[u].push_back(v);
            p[v].push_back(u);
        }
        Bfs(1);
        int posi=0,mmax=-1;
        for(int i=1; i<=n; i++)
        {
            if(dp[i]>mmax)
            {
                mmax=dp[i];
                posi=i;
            }
        }
        Bfs(posi);
        mmax=-1;
        for(int i=1; i<=n; i++)
        {
            if(dp[i]>mmax)
            {
                mmax=dp[i];
            }
        }
        for(int kk=0; kk<m; kk++)
        {
            scanf("%d",&k);
            if(k<=mmax+1)
                printf("%d\n",k-1);
            else
                printf("%d\n",mmax+(k-mmax-1)*2);
        }
    }
    return 0;
}

Hdu 4608  I-number   1009

题意:对于给定的x,要找一个y,要求y>x,且y所有位加和为10的倍数,输出最小的y

两个数之间的差值不会超过20,所以可以一直+1知道找到要找的数,>9就向高位进一位

代码:

#include <iostream>//I-number
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include<cmath>
#define INF 0x7fffffff
const long long M=1e9+7;
using namespace std;
char a[100005];
int s[100005];
int main()
{
    int t,i;
    scanf("%d",&t);
    getchar();
    while(t--)
    {
	memset(s,0,sizeof(s));
        scanf("%s",a);
        int l=strlen(a);
        int j=0;
        for(i=l-1; i>=0; i--)
        {
            s[j++]=a[i]-'0';
        }
        int sum;
        do
        {
            sum=0;
            s[0]+=1;
            for(i=0;i<l;i++)
            {
                s[i+1]+=s[i]/10;
                s[i]=s[i]%10;
            }
            if(s[l])
            l++;
            for(i=0;i<l;i++)
            sum+=s[i];
        }while(sum%10!=0);
        for(i=l-1;i>=0;i--)
        printf("%d",s[i]);
        printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值