• 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
  • 相关阅读:
    窗体间传值
    winform 导出datagridview 到excel
    单击单元格任意地方事件
    CLR via 随书笔记
    值类型和引用类型的区别
    System.Object简介
    装箱与拆箱
    静态类
    关于Linq2Sql有外键表的更新引发的问题。
    滑动切换页面
  • 原文地址:https://www.cnblogs.com/lsykk/p/13677915.html
Copyright © 2020-2023  润新知