首页 > 代码库 > 多校第九场:贪心+矩阵快速幂中间优化+线性递推&线段树递推

多校第九场:贪心+矩阵快速幂中间优化+线性递推&线段树递推

HDU 4968 Improving the GPA

思路:贪心的搞吧!比赛的时候想了好久,然后才发现了点规律,然后乱搞1A。

因为贪心嘛!大的情况就是刚开始每个人的分数都是最大的最小值,即绩点4.0的最低分数85,然后最后一个数设为剩余的分数,然后如果小于60就从第一个分数补到这个分数来,然后最后一个分数还小于60,那就用第二个补……依次往下搞,那时我也不知道这样就搞出答案了,我还没证明这个对不对呢,哈哈。

小的情况:小的情况就是先假设每个人都是绩点最小的最大分数,即绩点2.0的最大分数69,然后和最大的一样,如果最后一个分数>100的话,就把这个减去,把第一个加上,如果还大,就把第二个加上……乱搞一通就得答案了,还没证明对不对反正A了。

A了之后想想挺对的,贪心嘛,反正得到最大最小的就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define llson j<<1,l,mid
#define rrson j<<1|1,mid+1,r
#define INF 0x7fffffffffffffff
#define maxn 400050
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int main()
{
    //freopen("1.txt","r",stdin);
    int t,i;
    double b[101];
    for(i=60;i<=100;i++)
    {
        if(i>=60&&i<70) b[i]=2.0;
        else if(i>=70&&i<75) b[i]=2.5;
        else if(i>=75&&i<80) b[i]=3.0;
        else if(i>=80&&i<85) b[i]=3.5;
        else b[i]=4.0;
    }
    scanf("%d",&t);
    while(t--)
    {
        int s,n,a[11],sum;
        scanf("%d%d",&s,&n);
        sum=s*n;
        for(i=0;i<n;i++) a[i]=85;
        a[n-1]=sum-(n-1)*85;
        for(i=0;i<n-1;i++)
        {
            while(a[i]>60&&a[n-1]<60)
            {
                a[i]--,a[n-1]++;
            }
            while(a[i]<100&&a[n-1]>100)
            {
                a[i]++,a[n-1]--;
            }
        }
        double Max=0,Min=0;
        for(i=0;i<n;i++) Max+=b[a[i]];
//        for(i=0;i<n;i++)
//            cout<<a[i]<<' ';
//        cout<<endl;
        Max=Max/(n*1.0);
        for(i=0;i<n-1;i++)
        {
            a[i]=69;
            sum-=a[i];
        }
        a[n-1]=sum;
        for(i=0;i<n-1;i++)
        {
            while(a[i]>60&&a[n-1]<60)
            {
                a[i]--,a[n-1]++;
            }
            while(a[i]<100&&a[n-1]>100)
            {
                a[i]++,a[n-1]--;
            }
        }
        for(i=0;i<n;i++)
            Min+=b[a[i]];
//        for(i=0;i<n;i++)
//            cout<<a[i]<<' ';
//        cout<<endl;
        Min=Min/(n*1.0);
        printf("%.4f %.4f\n",Min,Max);
    }
    return 0;
}
HDU 4965 Fast Matrix Calculation

思路:这题刚开始的时候我敲了,然后因为一直在想n*n的矩阵乘法怎么简化,然后就没想到先搞(k*n)*(n*k)=(k*k)^(n*n-1)的快速幂矩阵,然后再乘以前面的简化。

因为由:(n*k)*(k*n)*(n*k)*(k*n)*(n*k)*(k*n)*……*(k*n),这样的话我们可以转换成: (n*k)*(((k*n)*(n*k))^(n*n-1))*(k*n),这样转换后,本来1000*1000的快速幂就可以转换成7*7的快速幂了,就不会T了。

但是大帝想到后,又敲了一遍,依然T不止,不知道T在哪了,因为k*k的快速幂应该不会超时了啊,怎么还会T呢?然后赛后琦神说k*k的和n*n的不能定义在同一个结构体里,因为都开了1000*1000的数组,然后每次幂都会遍历一次数组就T了,哎呀,又长见识了!所以在快速幂那只定义一个7*7的矩阵就行了,然后前面和后面处理才需要用到1000*1000的矩阵,然后这样搞出来的265ms。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define INF 6
typedef long long ll;
typedef unsigned long long ull;
#define maxn 1005
#define maxm 1005
using namespace std;
struct Matrix
{
    int n,m;
    int a[7][7];
    Matrix operator*(const Matrix &b) const
    {
        Matrix tmp;
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                tmp.a[i][j]=0;
        tmp.n=n;
        tmp.m=b.m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                if(a[i][j])
                    for(int k=0; k<b.m; k++)
                        tmp.a[i][k]=(tmp.a[i][k]+a[i][j]*b.a[j][k])%INF;
        return tmp;
    }
};
Matrix quick_pow(Matrix m,int k)
{
    Matrix tmp;
    tmp.n=m.n;
    tmp.m=m.m;
    for(int i=0; i<tmp.n; i++)
        for(int j=0; j<tmp.n; j++)
            if(i==j) tmp.a[i][j]=1;
            else tmp.a[i][j]=0;
    while(k)
    {
        if(k&1) tmp=tmp*m;
        k>>=1;
        m=m*m;
    }
    return tmp;
}
struct Matrix2
{
    int n,m;
    int a[maxn][maxn];
    Matrix2 operator*(const Matrix2 &b) const
    {
        Matrix2 tmp;
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                tmp.a[i][j]=0;
        tmp.n=n;
        tmp.m=b.m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                if(a[i][j])
                    for(int k=0; k<b.m; k++)
                        tmp.a[i][k]=(tmp.a[i][k]+a[i][j]*b.a[j][k])%INF;
        return tmp;
    }
};
Matrix2 aa,bb,a1,b1,res,tmp,c,d;
Matrix cc,ans;
int main()
{
    //freopen("1.txt","r",stdin);
    int n,k,i,j;
    while(scanf("%d%d",&n,&k)&&(n||k))
    {
        for(i=0; i<n; i++)
            for(j=0; j<k; j++)
                scanf("%d",&aa.a[i][j]);
        for(i=0; i<k; i++)
            for(j=0; j<n; j++)
                scanf("%d",&bb.a[i][j]);
        aa.n=n,aa.m=k,bb.n=k,bb.m=n;
        a1=aa,b1=bb;
        res=b1*a1;
        ans.n=k,ans.m=k;
        for(i=0; i<k; i++)
            for(j=0; j<k; j++)
                ans.a[i][j]=res.a[i][j];
        cc=quick_pow(ans,n*n-1);
        tmp.n=k,tmp.m=k;
        for(i=0; i<k; i++)
            for(j=0; j<k; j++)
                tmp.a[i][j]=cc.a[i][j];
        c=aa*tmp;
        d=c*bb;
        int sum=0;
        for(i=0; i<n; i++)
            for(j=0; j<n; j++)
                sum+=d.a[i][j];
        printf("%d\n",sum);
    }
    return 0;
}

HDU 4970 Killing Monsters

思路:官方题解的做法确实太机智了,从前往后扫描递推一遍,然后再从后往前扫描递推一遍就可以得出每个点的最终攻击值了。呀,比赛的时候确实没有想到,虽然这题大帝告诉我完题意的时候我也感觉应该不需要用线段树来做,但是也没有想到这个做法,所以最后宝哥还是用了线段树成段更新标记,单点查询过了。

解法一:

官方做法,线性O(n)。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define INF 6
#define maxn 100005
typedef long long ll;
typedef unsigned long long ull;
ll a[maxn];
int main()
{
    //freopen("1.txt","r",stdin);
    int n,m,q;
    while(scanf("%d",&n)&&n)
    {
        mem(a,0);
        scanf("%d",&m);
        int i,l,r,pos,sum=0;
        ll v;
        while(m--)
        {
            scanf("%d%d%I64d",&l,&r,&v);
            a[l]+=v,a[r+1]-=v;
        }
        for(i=2;i<=n;i++) a[i]+=a[i-1];
        for(i=n-1;i>=1;i--) a[i]+=a[i+1];
        scanf("%d",&q);
        while(q--)
        {
            scanf("%I64d%d",&v,&pos);
            if(a[pos]<v) sum++;
        }
        printf("%d\n",sum);
    }
    return 0;
}

解法二:

线段树成段更新,然后单点查询,单点查询的时候从前往后递推,然后在pos位置的攻击值就是sum[n]-sum[pos-1]的。其实很好理解,就是像暴力一样,每个区间都更新一个攻击值v,然后这个区间的点都是v,然后i从前往后递推就可以得到i到n的攻击值了,这样是暴力的更新,因为这样会T,所以用线段树标记一下不就过了嘛,不过时间比线性的多200多ms。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define llson j<<1,l,mid
#define rrson j<<1|1,mid+1,r
#define INF 6
#define maxn 400005
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
ll lazy[maxn],sum[maxn/4];
void pushdown(int i)
{
    if(lazy[i])
    {
        lazy[i<<1]+=lazy[i];
        lazy[i<<1|1]+=lazy[i];
        lazy[i]=0;
    }
}
void update(int i,int l,int r,int L,int R,int v)
{
    if(l==L&&r==R) {lazy[i]+=v;return;}
    int mid=(l+r)>>1;
    pushdown(i);
    if(R<=mid) update(lson,L,R,v);
    else if(L>mid) update(rson,L,R,v);
    else
    {
        update(lson,L,mid,v);
        update(rson,mid+1,R,v);
    }
}
void query(int i,int l,int r)
{
    if(l==r)
    {
        sum[l]=lazy[i]+sum[l-1];
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(i);
    query(lson);query(rson);
}
int main()
{
    //freopen("1.txt","r",stdin);
    int n,m,q;
    while(scanf("%d",&n)&&n)
    {
        mem(lazy,0);
        scanf("%d",&m);
        int i,l,r,pos,ans=0;
        ll v;
        while(m--)
        {
            scanf("%d%d%I64d",&l,&r,&v);
            update(1,1,n,l,r,v);
        }
        query(1,1,n);
        scanf("%d",&q);
        while(q--)
        {
            scanf("%I64d%d",&v,&pos);
            if(sum[n]-sum[pos-1]<v) ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}