寒假训练3

n^n的末位数字 (51Nod - 1004)

题意:传送门

思路与小结:

嗯,快速幂,然后只要最后一位数,于是我觉得可以不用longlong,但忘了先模再快速幂,WA了一次。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;

#define INF 0x3fffffff
#define MOD 10

long long n;

long long pow(long long x,long long y)
{
    long long t=1LL;
    while(y)
    {
        if(y&1)
            t*=x,t%=MOD;
        x*=x;
        x%=MOD;
        y=y>>1LL;
    }
    return t;
}

int main()
{
    scanf("%lld",&n);
    printf("%lld\n",pow(n,n));
}

蜥蜴和地下室 (51Nod - 1489)

题意:传送门

思路与小结

首先样例把我看懵了,然后发现原来血量为0不算死亡,一定要小于零。所以直接dfs暴力,考虑当前位置i,我们要保证位置i-1的弓箭手死了,所以有一个下界,又要保证是最少次数,所以有一个上界。在上界和下界之间进行枚举,然后递归进入下一个位置。数据特别小,暴力可以过。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
using namespace std;

#define INF 0x3fffffff
#define MAXN 50050

int em[110],ans,n,a,b;

void dfs(int x,int sum)
{
    if(sum>=ans)
        return;
    if(x==n-1)
    {
        int lasta=em[n-1]<0?0:em[n-1]/b+1,lastb=em[n-2]<0?0:em[n-2]/a+1;
        sum+=max(lasta,lastb);
        ans=min(ans,sum);
        return;
    }
    int l=em[x-1]<0?0:em[x-1]/b+1,r=em[x]<0?0:em[x]/a+1;
    r=max(l,r);
    for(int i=l;i<=r;i++)
    {
        em[x]-=a*i;
        em[x+1]-=b*i;
        dfs(x+1,sum+i);
        em[x]+=a*i;
        em[x+1]+=b*i;
    }
}

int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for(int i=0;i<n;i++)
        scanf("%d",&em[i]);
    ans=INF;
    dfs(1,0);
    printf("%d",ans);
}

前缀后缀集合 (51Nod - 1280)

题意:传送门

思路与小结

这道题一开始,我除了暴力是没有其他想法的。我的暴力自认为时间复杂度是 O(n2) O ( n 2 ) 妥妥地T掉,但是呢,我仔细一想似乎并不用那么复杂。我的方法是可以进行优化的,遂成功做到 O(n) O ( n ) ?(或者 O(nlog(n) O ( n ∗ l o g ( n ) )

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<map>
using namespace std;

#define INF 0x3fffffff
#define MAXN 50050

int n;
int a[MAXN];
long long ans;
map<int,int> vis;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int j=n,last=0,now=0;
    for(int i=1;i<=n;i++)
    {
        if(vis.count(a[i]))
        {
            ans+=now;
            continue;
        }
        vis[a[i]]=1;
        last++; now=0;
        while(j>=1&&vis.count(a[j]))
        {
            if(vis[a[j]]==1)
            {
                vis[a[j]]=2;
                last--;
            }
            if(last==0)
            {
                ans++;
                now++;
            }
            j--;
        }
    }
    printf("%lld\n",ans);
}

山峰和旗子 (51Nod - 1281)

题意:传送门

思路与小结

首先我们用 O(n) O ( n ) 的时间把山峰弄出来,然后我们可以发现答案是满足单调性的。然后二分答案就好了,版题。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
using namespace std;

#define INF 0x3fffffff
#define MAXN 50050

int n;
int a[MAXN];
vector <int> E;

bool check(int k)
{
    if(!k)
        return 1;
    if(k==1)
        return E.size()?1:0;
    int last=0;
    vector<int>::iterator it;
    for(int i=1;i<k;i++)
    {
        it=lower_bound(E.begin(),E.end(),last+k);
        if(it==E.end())
            return 0;
        last=*it;
    }
    return 1;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=2;i<n;i++)
        if(a[i]>a[i-1]&&a[i]>a[i+1])
            E.push_back(i-1);
    int l=0,r=n;
    while(r-l>1)
    {
        int mid=(l+r)>>1;
        if(check(mid))
            l=mid;
        else
            r=mid;
    }
    printf("%d",l);
}

最长等差数列 (51Nod - 1055)

题意:传送门

思路与小结

不得不说我的做题顺序有些慌乱,先做了F题后,就去写G题的线段树模版,然后发现自己没有更进一步的思路了。心想:难道这场考试就这样了?然后一点排名,发现我E题还没做,如果不是后来看了我应该不会意识到我没做。
这道题仔细想了一下,可以用Dp来做。利用等差中项的性质。
d[i][j]表示第i个数和第j个数都是等差数列中的数,然后这种情况下的最长等差数列的长度。
因为如果a[i]+a[k]==2*a[j],那么第k个数就可以和它们形成等差数列。d[j][k]=d[i][j]+1;最终答案取max就好了。
顺便说一下,先要排序。以及为了不爆内存,要用short int

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<map>
using namespace std;

#define INF 0x3fffffff
#define MAXN 10005

int n,ans=1;
int a[MAXN];
short int d[MAXN][MAXN];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    for(int i=2;i<=n;i++)
    {
        int j=i-1,k=i+1;
        while(j>0&&k<=n)
        {
            if(a[j]+a[k]>2*a[i])
                j--;
            else if(a[j]+a[k]<2*a[i])
                k++;
            else
            {
                if(d[j][i]==0)
                    d[j][i]=d[i][k]=3;
                else
                    d[j][i]=d[i][k]=d[j][i]+1;
                ans=max(ans,(int)d[i][k]);
                j--,k++;
            }
        }
    }
    printf("%d",ans);
}

零树 (51Nod - 1424)

题意:传送门

思路与小结

对于一个节点,我们先考虑它的儿子,然后再对剩下的部分进行操作。开两个数组,一个是+1的次数,一个是-1的次数,分别通过儿子中最大的那一个转移过来。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<map>
using namespace std;

#define INF 0x3fffffff
#define MAXN 100050

int n;
long long d[MAXN],up[MAXN],down[MAXN];
vector<int> E[MAXN];

void dfs(int x,int f)
{
    for(int i=0;i<E[x].size();i++)
    {
        int t=E[x][i];
        if(t!=f)
        {
            dfs(t,x);
            up[x]=max(up[x],up[t]);
            down[x]=max(down[x],down[t]);
        }
    }
    d[x]+=up[x]-down[x];
    if(d[x]>0)
        down[x]+=d[x];
    if(d[x]<0)
        up[x]-=d[x];
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        E[a].push_back(b);
        E[b].push_back(a);
    }
    for(int i=1;i<=n;i++)
        scanf("%lld",&d[i]);
    dfs(1,-1);
    printf("%lld",up[1]+down[1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值