2019杭电多校第六场补题

补了四题:1002,1005,1006,1011

1002:Nonsense Time

先求总的LIS,记录一个LIS。然后考虑时间倒流,每次删除一个点,如果这个点在求得的LIS里面,就暴力重新求一次LIS。因为数据随机,所以LIS期望长度是 n \sqrt{n} n ,求一次LIS复杂度 O ( n l o g n ) O(nlogn) O(nlogn),总复杂度 O ( n n l o g n ) O(n\sqrt{n}logn) O(nn logn)
神奇的一题。

#include<bits/stdc++.h>
#define P pair<int, int>
using namespace std;
const int maxn = 1e5 + 50;
int k[maxn], a[maxn];
int pre[maxn];
int vis[maxn];
int del[maxn];
vector<P> b;
int n;
int work()
{
    b.clear();
    for(int i = 0; i < n; ++i) vis[i] = 0;
    for(int i = 0; i < n; ++i){
        if(del[i]) continue;
        int p = lower_bound(b.begin(),b.end(),P(a[i], i)) - b.begin();
        if(p == b.size()) b.push_back(P(a[i], i));
        else b[p] = P(a[i],i);
        if(p == 0) pre[i] = -1;
        else pre[i] = b[p-1].second;
    }
    if(b.size() == 0) return 0;
    int p = b[b.size()-1].second;
    while(p!=-1) vis[p] = 1, p = pre[p];
    return b.size();
}
void init()
{
    scanf("%d", &n);
    memset(del,0, (n+1)<<2);
    for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
    for(int i = 0; i < n; ++i) scanf("%d", &k[i]), k[i]--;
}
int ans[maxn];
void sol()
{
    int len = work();
    for(int i = n-1; i >= 0; --i){
        ans[i] = len;
        del[k[i]] = 1;
        if(vis[k[i]]) len = work();
    }
    for(int i = 0; i < n; ++i){
        if(i > 0) printf(" ");
        printf("%d",ans[i]);
    }printf("\n");
}
int main()
{
    int T;cin>>T;
    while(T--){
        init();sol();
    }
}

1005:Snowy Smile

把每个点按x排序,然后枚举最左边的点作为左边界,然后依次按x从小到大加入点,每加入一个点,最右边可到达的边界的扩展一点。把y离散化,记录第i行加入点的总价值为Y[i],每次扩展右边界后求一下Y数组的最大子段和来更新答案。
需要维护一个可修改的最大子段和。

#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 2e3 + 50;
int cc[maxn];
int num, n;
struct node{
    int x, y;
    ll w;
    bool operator < (const node& a)const{return x < a.x;}
}e[maxn];
ll sum[maxn<<2], lv[maxn<<2], rv[maxn<<2], v[maxn<<2];
void up(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    v[rt] = max(v[rt<<1], v[rt<<1|1]);
    v[rt] = max(v[rt], rv[rt<<1]+lv[rt<<1|1]);
    lv[rt] = max(lv[rt<<1], sum[rt<<1] + lv[rt<<1|1]);
    rv[rt] = max(rv[rt<<1|1], rv[rt<<1] + sum[rt<<1|1]);
    return;
}
void build(int rt, int l, int r){
    sum[rt] = lv[rt] = rv[rt] = v[rt] = 0;
    if(l == r) return;
    build(lson); build(rson);
}
void add(int rt, int l, int r, int id, ll w){
    if(l == r){
        sum[rt] += w;
        v[rt] = lv[rt] = rv[rt] = sum[rt];
        return;
    }
    if(id <= mid) add(lson, id, w);
    else add(rson, id, w);
    up(rt);
}
void init()
{
    scanf("%d", &n);
    num = 0;
    for(int i = 0; i < n; ++i){
        scanf("%d%d%lld", &e[i].x, &e[i].y, &e[i].w);
        cc[++num] = e[i].y;
    }
    sort(cc+1,cc+1+num); num = unique(cc+1,cc+1+num)- cc-1;
    sort(e, e+n);
    for(int i = 0; i < n; ++i) e[i].y = lower_bound(cc+1,cc+1+num,e[i].y) - cc;
    e[n].x = 0x3f3f3f3f;
}
ll work(int id){//×ó±ß½ç
    ll res = 0;
    build(1, 1, num);
    for(int i = id; i < n; ++i){
        add(1, 1, num, e[i].y, e[i].w);
        if(e[i].x != e[i+1].x){
            res = max(res, v[1]);
        }
    }return res;
}
void sol()
{
    ll ans = 0;
    for(int i = 0; i < n; ++i){
        if(i!=0 && e[i].x == e[i-1].x) continue;
        ans = max(ans, work(i));
    }
    printf("%lld\n", ans);
}
int main()
{
	int T;cin>>T;
	while(T--){
        init();
        sol();
	}
}

1006:Faraway

n个点一共把平面分割成了n^2个部分,对于每一个部分,枚举xe%60和ye%60的值,判断可不可行,如果可行,这个部分内有几个满足的(xe,ye)。最后把每个部分的答案加起来。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n, m;
struct node{
    int x, y, k, t;
}e[15];
int a[15], ca;
int b[15], cb;
void init()
{
    scanf("%d%d", &n, &m);
    a[1] = m+1, b[1] = m+1;
    ca = cb = 1;
    for(int i = 0; i < n; ++i)
        scanf("%d%d%d%d", &e[i].x, &e[i].y, &e[i].k, &e[i].t), a[++ca] = e[i].x, b[++cb] = e[i].y;
    sort(a, a+ca+1);
    sort(b, b+cb+1);
}
bool check(int x, int y)
{
    for(int i = 0; i < n; ++i){
        if((abs(x-e[i].x) + abs(y-e[i].y))%e[i].k != e[i].t) return false;
    }
    return true;
}
ll cal(int l, int r){
    if(r-l <= 0) return 0;
    return (r-l-1)/60 + 1;
}
void sol()
{
    ll ans = 0;
    for(int i = 0; i < ca; ++i){
        for(int j = 0; j < cb; ++j){
            for(int x = 0; x < 60; ++x){
                if(x < a[i+1])
                for(int y = 0; y < 60; ++y){
                    if(y < b[j+1]){
                        if( check(a[i]+x, b[j]+y) )
                            ans = ans + cal(a[i]+x, a[i+1])*cal(b[j]+y, b[j+1]);
                    }
                }
            }
        }
    }
    printf("%lld\n", ans);
}
int main()
{
    int T;cin>>T;
    while(T--){
        init();sol();
    }
}

1011:11 Dimensions

没能get到题解的科学写法,不过受到dls的启发写了一个看上去就很对的写法。
因为只考虑32个问号就可以构成 1 0 32 10^{32} 1032个数字了(这数量得用__int128存),所以我们有理由相信,如果在这 1 0 32 10^{32} 1032个数字里面都没找到第k个满足%m等于0的那个数字,那么它就无解。(因为k<=1e18)。
所以我们只找最后32个‘?’,而其他的‘?’都当成0处理。用dp[i][j]表示填了后i+1个问号,模m等于j的不同填法的数目。对于每个查询,只要dfs到底部即可找到答案。

#include<bits/stdc++.h>
#define ll long long
#define P pair<ll, int>
using namespace std;
const int maxn = 1e5 + 50;
const ll mod = 1e9 + 7;
__int128 dp[55][21];
char s[maxn];
int n, m, Q;
ll bin[maxn];//%m
ll pp[maxn];//%mod
int id[55], cnt;
ll Ans, k;
__int128 dfs(int pos, ll res, __int128 sum, ll val){//位置,需要的余数,当前计数值,前面累计值
//    cout<<"p:"<<pos<<" res:"<<res<<" sum:"<<sum<<endl;
//    cout<<"Ans:"<<Ans<<endl;
    if(pos == -1){//搜到了底部
        if(sum + 1 == k && res == 0){
            Ans = val;
            return 1;
        }
        else if(res == 0) return 1;
        else return 0;
    }
    if(dp[pos][res] != -1){
        if(Ans != -1) return dp[pos][res];
        if(sum + dp[pos][res] < k) return dp[pos][res];
    }
   __int128 ans = 0;
    for(ll x = 0; x <= 9; ++x){
        ans += dfs(pos-1, ((res-x*bin[n-1-id[pos] ])%m + m)%m, sum+ans, (val + x*pp[n-1-(id[pos])])%mod);
        if(dp[pos][res]!=-1 && Ans != -1) return dp[pos][res];
    }
    dp[pos][res] = ans;
    return dp[pos][res];
}
void sol(ll sum ,ll res)//当前总值,当前余数
{
    //cout<<"sum:"<<sum<<" res:"<<res<<endl;
    while(Q--){
        scanf("%lld", &k);
        Ans = -1;
        dfs(cnt-1, (m-res)%m, 0, sum);
        printf("%lld\n", Ans);
    }
}
int main()
{
    //freopen("1011.in", "r", stdin);
    int T;cin>>T;
    while(T--){
        scanf("%d%d%d", &n, &m, &Q);
        scanf("%s", s);
        bin[0] = 1, pp[0] = 1;
        for(int i = 1; i <= n; ++i) bin[i] = bin[i-1]*10%m, pp[i] = pp[i-1]*10%mod;
        int pos = n-1;
        cnt = 0;
        ll sum = 0, res = 0;
        while(pos >= 0 && cnt < 32){
            if(s[pos] != '?') {
                int x = s[pos]-'0';
                sum = (sum + pp[n-1-pos]*x)%mod;
                res = (res + bin[n-1-pos]*x)%m;
            }
            else {
                id[cnt++] = pos;
            }
            pos--;
        }
        for(int i = 0; i <= pos; ++i) {
            if(s[i] == '?') continue;
            sum = (sum + pp[n-1-i]*(s[i] - '0'))%mod;
            res = (res + bin[n-1-i]*(s[i]-'0'))%m;
        }
        for(int i = 0; i < cnt; ++i) for(int j = 0; j < m; ++j) dp[i][j] = -1;
        sol(sum, res);
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值