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

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;
}

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

时间: 2024-07-30 04:26:23

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

POJ3735 Training little cats DP,矩阵快速幂,稀疏矩阵优化

题目大意是,n只猫,有k个动作让它们去完成,并且重复m次,动作主要有三类gi,ei,s i j,分别代表第i只猫获得一个花生,第i只猫吃掉它自己所有的花生,第i只和第j只猫交换彼此的花生.k,n不超过100,m不超过1000,000,000,计算出最后每只猫还剩下多少个花生. 我们假设一个n维向量P,每个分量的值代表这n只猫所拥有的花生数,那么对于gi操作其实就是在第i维分量上加上1:对于ei,那就是在第i维分量上乘以0,说到这里,有木有感觉这很像3D坐标转化中的平移矩阵和缩放矩阵?没错,就是这

POJ 3150 Cellular Automaton --矩阵快速幂及优化

题意:给一个环,环上有n块,每块有个值,每一次操作是对每个点,他的值变为原来与他距离不超过d的位置的和,问k(10^7)次操作后每块的值. 解法:一看就要化为矩阵来做,矩阵很好建立,大白书P157页有讲,大概为: [1 1 0 .. 0 1] [1 1 1 .. .. 0] ... [1 1 .. .. .. 1]  的循环矩阵,可以证明,循环矩阵的乘积还是循环矩阵,且循环矩阵的性质: a[i][j] = a[i-1][j-1] (循环的) ,所以,我们每次矩阵相乘只需要算出第一行,余下的不需要

「常系数齐次线性递推」——矩阵快速幂的优化

引入: 对于递推方程: $$F(x) = \sum_{i=1}^k a_iF(x-i)$$ 我们显然会得到一个关于$F$的多项式求逆或者矩阵递推式,大多数情况下我们都是用后者,但是当$k$很大的时候,$k^3log n$的时间复杂度我们是吃不消的,那么自然我们的前人就搞出了一些优化. 特征多项式及Cayley-Hamilton定理: 一.特征多项式的定义: 设$A$是$n$阶矩阵,若数$\lambda$和非零列向量$x$使关系式$$Ax=\lambda x\;\;\;\;\;(1)$$ 成立,那

hdu 1575 Tr A(矩阵快速幂乘法优化算法)

Problem Description A为一个方阵,则Tr A表示A的迹(就是主对角线上各项的和),现要求Tr(A^k)%9973. Input 数据的第一行是一个T,表示有T组数据. 每组数据的第一行有n(2 <= n <= 10)和k(2 <= k < 10^9)两个数据.接下来有n行,每行有n个数据,每个数据的范围是[0,9],表示方阵A的内容. Output 对应每组数据,输出Tr(A^k)%9973. Sample Input 2 2 2 1 0 0 1 3 99999

zoj3690--Choosing number(dp,矩阵快速幂)

Choosing number Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Submit Status Description There are n people standing in a row. And There are m numbers, 1.2...m. Every one should choose a number. But if two persons standing

UVA 11651 - Krypton Number System(DP+矩阵快速幂)

UVA 11651 - Krypton Number System 题目链接 题意:给一个进制base,一个分数score求该进制下,有多少数满足一下条件: 1.没有连续数字 2.没有前导零 3.分数为score,分数的计算方式为相邻数字的平方差的和 思路:先从dp入手,dp[i][j]表示组成i,最后一个数字为j的种数,然后进行状态转移,推出前面一步能构成的状态,也就是到dp[(b - 1) * (b - 1)][x]. 然后可以发现后面的状态,都可以由前面这些状态统一转移出来,这样就可以利用

2015微软编程之美资格赛骨牌覆盖(矩阵快速幂)

由于棋盘只有两行,所以如果第i列的骨牌竖着放,那么就转移为第1列到第i-1列骨牌有多少种摆法 如果第一行第i列骨牌横着放,那么第二行第i列也要横着放,那么就转移为了第1列到第i-2列骨牌有多少种方法 dp[i] = dp[i-1] + dp[i-2],但是列数太多了. 这种递推的算式可以用矩阵快速幂来优化 所以时间复杂度瞬间变为O(logn) 1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h>

2016 pku campusH/OpenJ_POJ - C16H(推公式+矩阵快速幂)

传送门:http://poj.openjudge.cn/practice/C16H?lang=en_US 题面:描述 Wenwen has a magical ball. When put on an infinite plane, it will keep duplicating itself forever. Initially, Wenwen puts the ball on the location (x0, y0) of the plane. Then the ball starts

hihoCoder#1743:K-偏差排列(矩阵快速幂+状压dp)

题意: 如果一个 \(1\to N\) 的排列 \(P=[P_1, P_2, ... P_N]\) 中的任意元素 \(P_i\) 都满足 \(|P_i-i| ≤ K\) ,我们就称 \(P\) 是 \(K\)-偏差排列. 给定 \(N\) 和 \(K\) ,请你计算一共有少个不同的排列是 \(K\)-偏差排列. 例如对于 \(N=3\) ,有 \(3\) 个 \(1\)-偏差排列:\([1, 2, 3], [1, 3, 2], [2, 1, 3]\). 由于答案可能非常大,你只需要输出答案模 \