2018CCPC网络赛 练习总结

网络赛好难。。 看半天一题都写不出来。。

C    HDU 6440

题意:给出质数p,定义运算加与乘,使得(m+n)p=mp+np成立,且存在0<q<p,使得集合{qk|0<q<p}与集合{k|0<q<p}相等。

思路:由费马小定理可知,对于质数p,有ap-1 ≡ 1(mod p),

于是有(m+n)p ≡ m+n(mod p),m≡ m(mod p),n≡ n(mod p),

所以原来的加法乘法基础上模p即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
    int i,j,T,p;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&p);
        for (i=0;i<p;i++)
        {
            for (j=0;j<p;j++) printf("%d ",(i+j)%p);
            printf("
");
        }
        for (i=0;i<p;i++)
        {
            for (j=0;j<p;j++) printf("%d ",i*j%p);
            printf("
");
        }
    }
    return 0;
 } 
View Code

D    HDU 6441

题意:给出n,a,求正整数b,c使得an+bn=cn

思路:由费马大定理可知,an+bn=cn在n>2时无正整数解。

于是对于n>2和n==0的情况输出无解,对于n==1的情况输出1 a+1,对于n==2的情况构造勾股数。

构造勾股数:当a==2n+1,有b=2n2+2n,c=2n2+2n+1,使得a2+b2=c2

当a==2n,有b=n2-1,c=n2+1,使得a2+b2=c2。(n为正整数)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int main()
{
    int T,n,a;
    ll x;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&a);
        if (n>2 || n==0) printf("-1 -1
");
        else if (n==1) printf("%d %d
",1,a+1);
        else 
        {
            x=a/2;
            if (a&1) printf("%lld %lld
",2*x*x+2*x,2*x*x+2*x+1);
            else  printf("%lld %lld
",x*x-1,x*x+1);
        }
    }
    return 0;
 } 
View Code

G    HDU 6444

题意:给一个n长的环,从位置i走一步就走到位置(i+k)%n,每走到一个格子i就能得到价值a[i],最多走m步。初始时可以从任意位置开始。问要想最后的总价值不小于s,那么出发时的初始价值最小为多少。

思路:(这题我能WA10多次,调两晚上,就TM离谱)

很明显走的路线是一个环。

对于每一个格子求出它所在的环的路线,设路线长度为cnt,求出路线价值的前缀数组sum。

假设solve(x)求的是sum中区间长度不超过x的最大区间和。

(这个可以用单调队列实现,好像也有其他方法,不过说起来list是真方便)

若sum[cnt]<=0,那么此时的所能从格子中得到的最大价值就是solve(min(cnt,m))。

若sum[cnt]>0,分两种情况讨论。

1、若m<cnt,即m/cnt==0。此时的最大价值就是solve(m)。

2、若m>=cnt,即m/cnt>0。此时的最大价值有两种可能。

一种是走m/cnt个整圈,再走m%cnt步,最大价值就是(m/cnt)*sum[cnt]+solve(m%cnt);

另一种是走m/cnt-1个整圈,再走cnt步,最大价值就是(m/cnt-1)*sum[cnt]+solve(cnt)。

最后,输出max(0,s-ans)即可。

#include<cstdio>
#include<cstring>
#include<list>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=10005;
int vis[maxn];
ll a[maxn],g[maxn],sum[maxn<<1],cnt;
list<ll>q;
ll solve(int m)
{
    if (m==0) return 0;
    q.clear(); 
    q.push_back(0);    
    ll re=0;
    for (int i=1;i<=2*cnt;i++)
    {
        while (!q.empty() && sum[q.back()]>sum[i]) q.pop_back();
        q.push_back(i);
        while (!q.empty() && i-q.front()>m) q.pop_front();
        re=max(re,sum[i]-sum[q.front()]);
    }
    return re;
}
int main()
{
    int i,j,t,T;
    ll n,m,k,s,ans,div,res;
    scanf("%d",&T);
    for (t=1;t<=T;t++)
    {
        scanf("%lld%lld%lld%lld",&n,&s,&m,&k);
        for (i=0;i<n;i++) 
        {
            scanf("%lld",&a[i]);
            vis[i]=0;
        }
        ans=0;
        for (i=0;i<n;i++)
        {
            if (vis[i]) continue;
            cnt=0;
            for (j=i;vis[j]==0;j=(j+k)%n)//循环节上的所有元素  
            
            {
                vis[j]=1;
                g[++cnt]=a[j];
            }
            sum[0]=0;
            for (j=1;j<=cnt;j++) sum[j]=sum[j-1]+g[j];
            for (j=1;j<=cnt;j++) sum[j+cnt]=sum[j]+sum[cnt];//前缀和 
            if (sum[cnt]<=0) ans=max(ans,solve(min(m,cnt)));
            else 
            {
                div=m/cnt;
                res=m%cnt;
                ll mx1=solve(cnt);
                ll mx2=solve(res);
                if (div>0) 
                {    
                    ans=max(ans,(div-1)*sum[cnt]+mx1);
                    ans=max(ans,div*sum[cnt]+mx2);
                }
                else ans=max(ans,mx2);
            }
        }
        if (s-ans>0) ans=s-ans;
        else ans=0;
        printf("Case #%d: %lld
",t,ans);
    }
    return 0;
 } 
View Code

J    HDU 6447

题意:给一个1e9*1e9的地图,从(0,0)出发,走到(1e9,1e9),每次只能走(1,0),(0,1)或(1,1)。地图上有n个(n<=1e5)宝物,第i个宝物有价值w[i]。若宝物在(x,y),只能从(x-1,y-1)走才能获得这个宝物。问能够获得的宝物的最大总价值。

思路:用dp[i][j]表示走到当前格子所能获得的最大价值,可以想到DP公式dp[i][j]=max(dp[i-1][j],dp[i-1][j],dp[i-1][j-1]+w),

直接做复杂度不对,应该想到用数据结构优化。

首先将宝物坐标x离散化,接着按照y的大小为宝物排序。

对于每个宝物,查询所有x比它小的宝物(因为已经按y排过序,y也比它小)的dp中的最大值mx,

用mx+w更新答案,然后更新dp[x]=mx+w。

明显这维护的是一个区间最大值(以离散化后的x为下标),使用树状数组或者线段树都可以。

(然而我线段树写挂了,也不知道为什么。。)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005;
int c[maxn],n;
struct node
{
    int x,y,w;
}e[maxn];
bool cmp1(node a,node b){return a.x<b.x;}
bool cmp2(node a,node b)
{
    if (a.y==b.y) return a.x>b.x;
    return a.y<b.y;
}
int lowbit(int x){return x&(-x);}
void update(int x,int val) 
{
    for (int i=x;i<=n;i+=lowbit(i)) c[i]=max(c[i],val);
}
int query(int x) 
{
    int i,re=0;
    for (i=x;i;i-=lowbit(i)) re=max(re,c[i]);
    return re;
}
int main()
{
    int i,j,T,last,cnt,tmp,ans;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        for (i=1;i<=n;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
        last=-1;
        cnt=0;
        sort(e+1,e+n+1,cmp1);
        for (i=1;i<=n;i++)
        {
            if (e[i].x==last) e[i].x=cnt;
            else e[i].x=++cnt;
            last=e[i].x;
        }
        sort(e+1,e+n+1,cmp2);
        memset(c,0,sizeof(c)); 
        ans=0;
        for (i=1;i<=n;i++)
        {
            tmp=query(e[i].x-1)+e[i].w;
            ans=max(ans,tmp);
            update(e[i].x,tmp);
        }
        printf("%d
",ans);
    }
    return 0;
 } 
View Code
原文地址:https://www.cnblogs.com/lsykk/p/13677915.html