2019杭电多校第六场

1005 Snowy Smile

考虑朴素的最大子矩阵和
首先枚举上下边界,然后问题就转化成了维护最大子段和
这个问题可以用线段树解决
m a x v maxv maxv p r e v prev prev s u f v sufv sufv分别表示当前区间的最大值、当前区间紧贴左端点的最大值、当前区间紧贴右端点的最大值
单点修改时间复杂度位 O ( l o g N ) O(logN) O(logN),查询复杂度为 O ( 1 ) O(1) O(1)
因为总共只有 O ( N ) O(N) O(N)个点,所以时间复杂度是 O ( N 2 + N l o g N ) O(N^2+NlogN) O(N2+NlogN)

#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define MOD 1000000007
#define N 2005
#define ll long long
using namespace std;
ll sum[N*4],maxv[N*4],Prev[N*4],sufv[N*4];
void pushup(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1]; 
    maxv[rt] = max(maxv[rt<<1],maxv[rt<<1|1]);
    maxv[rt] = max(maxv[rt],sufv[rt<<1]+Prev[rt<<1|1]);
    Prev[rt] = max(Prev[rt<<1],sum[rt<<1]+Prev[rt<<1|1]);
    sufv[rt] = max(sufv[rt<<1|1],sum[rt<<1|1]+sufv[rt<<1]);
}
void build(int rt,int l,int r)
{
    if (l == r) {maxv[rt] = Prev[rt] = sufv[rt] = sum[rt] = 0; return;}
    int mid = (l + r) >> 1;
    build(rt<<1,l,mid); build(rt<<1|1,mid+1,r);
    pushup(rt);
}
void update(int rt,int l,int r,int pos,ll v)
{
    if (l == r) {sum[rt] += v; maxv[rt] = Prev[rt] = sufv[rt] = max(0ll,sum[rt]); return;}
    int mid = (l + r) >> 1;
    if (pos <= mid) update(rt<<1,l,mid,pos,v);    else update(rt<<1|1,mid+1,r,pos,v);
    pushup(rt); 
}
ll query() {return maxv[1];}
struct node
{
    int x,y,c;
    bool operator < (const node &rhs) const
    {
        return x<rhs.x || (x==rhs.x && y<rhs.y);
    }
}p[N];
int main()
{
    int t,n;
    scanf("%d",&t);
    while (t--)
    {
        vector <int> keyx;
        vector <int> keyy; 
        scanf("%d",&n);
        for (int i=0;i<n;i++)
        {
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].c);
            keyx.push_back(p[i].x);
            keyy.push_back(p[i].y);
        }
        sort(keyx.begin(),keyx.end());
        sort(keyy.begin(),keyy.end());
        int pp=unique(keyx.begin(),keyx.end())-keyx.begin();
        int qq=unique(keyy.begin(),keyy.end())-keyy.begin();
        for (int i=0;i<n;i++)
        {
            p[i].x=lower_bound(keyx.begin(),keyx.begin()+pp,p[i].x)-keyx.begin()+1;
            p[i].y=lower_bound(keyy.begin(),keyy.begin()+qq,p[i].y)-keyy.begin()+1;
        }
        sort(p,p+n);
        ll ans=0;
        for (int i=0;i<pp;i++)
        {
            int x=i+1;
            int tmp=0;
            while (tmp<n && p[tmp].x<x) tmp++;
            build(1,1,qq);
            while (tmp<n)
            {
                int curx=p[tmp].x;
                while (tmp<n && p[tmp].x==curx)
                {
                    update(1,1,qq,p[tmp].y,p[tmp].c);
                    tmp++;
                }
                ans=max(ans,query());
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

1006 Faraway

根据绝对值的定义,我们可以把这个平面分成 O ( N 2 ) O(N^2) O(N2)个区域,并且在区域内的点计算答案的时候都可以把绝对值拆掉
因为 l c m ( 2 , 3 , 4 , 5 ) = 60 lcm(2,3,4,5)=60 lcm(2,3,4,5)=60,所以一个区域内的点一定是以 60 ∗ 60 60*60 6060作为循环节
所以对每个区域,首先暴力做出 60 ∗ 60 60*60 6060的情况,然后把边边角角的算上即可
时间复杂度是 O ( 6 0 2 N 2 ) O(60^2N^2) O(602N2)

#include<iostream>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define N 20
using namespace std;
struct www{int x,y,k,t;} v[N];
int n,m,numx,numy,num,T,i,j,k1,k2,x1,y1,x2,y2;
long long res;
int f[100][100],h[100],w[100];
bool cmp1(const www &a,const www &b) {return a.x < b.x;}
bool cmp2(const www &a,const www &b) {return a.y < b.y;}
bool check(int x,int y)
{
    int i;
    fo(i,1,n) if ((abs(x-v[i].x)+abs(y-v[i].y))%v[i].k != v[i].t) return false;
    return true;
}
void add(int x1,int y1,int x2,int y2)
{
    int i,j,x,y;
    fo(i,1,60)
    fo(j,1,60)
    {
        x = x1 + i - 1; y = y1 + j - 1;
        if (check(x,y)) f[i][j] = 1; else f[i][j] = 0;
    }
    numx = (x2-x1+1) / 60; numy = (y2-y1+1) / 60;
    num = 0; fo(i,1,60) fo(j,1,60) num += f[i][j];
    res += 1ll * numx * numy * num;
    num = 0; fo(i,1,(x2-x1+1) % 60) fo(j,1,60) num += f[i][j];
    res += 1ll * numy * num;
    num = 0; fo(i,1,60) fo(j,1,(y2-y1+1) % 60) num += f[i][j];
    res += 1ll * numx * num;
    num = 0; fo(i,1,(x2-x1+1)%60) fo(j,1,(y2-y1+1)%60) num += f[i][j];
    res += 1ll * num;
    return;
}
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);
        fo(i,1,n) scanf("%d%d%d%d",&v[i].x,&v[i].y,&v[i].k,&v[i].t);
        //res = 0; add(0,0,m,m); cout<<res<<"std"<<endl; res = 0;
        sort(v+1,v+n+1,cmp1);
        k1 = 1; h[1] = v[1].x; fo(i,2,n) if (v[i].x != v[i-1].x) h[++k1] = v[i].x;
        sort(v+1,v+n+1,cmp2);
        k2 = 1; w[1] = v[1].y; fo(i,2,n) if (v[i].y != v[i-1].y) w[++k2] = v[i].y;
        res = 0;
        fo(i,0,k1)
        {
            x1 = h[i]; x2 = h[i+1]-1; if (i == k1) x2 = m;
            fo(j,0,k2)
            {
                y1 = w[j]; y2 = w[j+1]-1; if (j == k2) y2 = m;
                add(x1,y1,x2,y2);
                //cout<<x1<<" "<<y1<<" "<<x2<<" "<<y2<<" "<<res<<endl;
            }
        }
        cout<<res<<endl;
    }
    return 0;
}

1008 TDL

枚举 f ( n , m ) − n f(n,m)-n f(n,m)n的值,因为这个值不可能很大
然后反向推出 n n n,check一下是否是合法答案

1011 11 Dimensions

首先一个非常显然的结论就是不可能有很多 ′ ? ′ &#x27;?&#x27; ?
其实只要暴力做最后50~100个 ′ ? ′ &#x27;?&#x27; ?就好了,前面的 ′ ? ′ &#x27;?&#x27; ?全部可以填0
当然你也可以在DP大于1e18的时候直接break
这种题的经典做法就是考虑每一位填某个数之后的后继所有方案数
比如假设这一位填 3 3 3的时候有 x x x种方案,如果x比k小就填3,否则用k减去x,然后继续枚举456789
首先把所有非 ′ ? ′ &#x27;?&#x27; ?的数的贡献算出来,然后扔掉
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示填到第 i i i ′ ? ′ &#x27;?&#x27; ?,当前的模为 j j j,并且这一位填 k k k的方案数
然后从低位往高位转移就好
填的时从高位往地位确定
还有就是小心爆longlong

#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define MOD 1000000007
#define N 50005
using namespace std;
int T,n,m,q,i,j,num,flag,k1,k2;
char ch[N];
int a[N];
__int128_t ans[N],e[N],f[N][25][15];
__int128_t res,s,limit;
long long ress,k,rest_init,rest;
void init()
{
    res = 0;
    fo(i,1,n) if (a[i] == -1) res = (res * 10) % MOD; else res = (res * 10 + a[i]) % MOD;
    ans[n] = 1; fd(i,n-1,1) ans[i] = (ans[i+1] * 10) % MOD;
    s = 1; fd(i,n,1) {e[i] = s; s = (s * 10) % m;}
    fo(i,1,n+1) fo(j,0,m-1) fo(k1,0,9) f[i][j][k1] = 0;
    rest = 0;
    fo(i,1,n) if (a[i] > 0) rest = (rest + a[i] * e[i]) % m;
    rest_init = rest; 
}
int main()
{
    //freopen("1.in","r",stdin); 
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d%d",&n,&m,&q);
        scanf("%s",ch);
        fo(i,1,n) if (ch[i-1] == '?') a[i] = -1; else a[i] = ch[i-1] - '0';
        //num = 0; fo(i,1,n) if (a[i] == -1) num++;
        //fo(i,1,n) if (a[i] == -1) if (num > 100) a[i] = 0; else break;
        init();
        
        num = 0;
        fo(i,1,n)
        if (a[i] == -1)
        {
            num++;
            ans[num] = ans[i];
            e[num] = e[i];
        }
        
        n = num;
        flag = 0;
        f[n+1][0][0] = 1;
        fd(i,n,1)
        {
            fo(j,0,m-1)
            fo(k1,0,9)
            fo(k2,0,9)
                f[i][(j+e[i]*k1)%m][k1] = f[i][(j+e[i]*k1)%m][k1] + f[i+1][j][k2];
            fo(j,0,m-1) fo(k1,0,9) if (f[i][j][k1] > (__int128_t)1e18) {flag = 1; break;}
            if (flag) break;
        }
        if (flag == 0) flag = 1; else flag = i;
        while (q--)
        {
            scanf("%lld",&k);
            rest = rest_init;
            limit = 0;
            fo(j,0,9) limit += f[flag][(m-rest)%m][j];
            if ((__int128_t)k > limit) {printf("-1\n"); continue;}
            long long ress = res;
            fo(i,flag,num)
            {
                fo(j,0,9)
                {
                    if ((__int128_t)k > f[i][(m-rest)%m][j]) k -= f[i][(m-rest)%m][j]; else break;
                }
                ress = (ress + j * ans[i]) % MOD;
                rest = (rest + j * e[i]) % m; 
            }
            printf("%lld\n",ress);
        }
    }
    return 0;
}

1012 Stay Real

一个显然的贪心是每次暴力取最大的
所以用一个优先队列维护所有的叶子结点,如果取走了最后一个叶子结点就把父亲节点加入优先队列

#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct node
{
    int a,b;
    bool operator < (const node &rhs) const
    {
        return b<rhs.b;
    } 
};
int T,n,i,op,x;
int a[205000];
long long sum[5];
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        fo(i,1,n) a[i] = a[i*2] = a[i*2+1] = -1;
        fo(i,1,n) scanf("%d",&a[i]);
        priority_queue<node> q;
        fo(i,1,n)
            if (a[i*2] == -1 && a[i*2+1] == -1)
                q.push(node{i,a[i]});
        sum[0] = sum[1] = 0;
        op = 0;
        while (!q.empty())
        {
            x = q.top().a; q.pop();
            sum[op] = sum[op] + a[x]; a[x] = -1;
            x = x / 2;
            if (a[x*2] == -1 && a[x*2+1] == -1) q.push({x,a[x]});
            op ^= 1;
        }
        cout<<sum[0]<<" "<<sum[1]<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值