8.7 Round 2

 刚说完白天思维性弱,晚上就来三道思维题...

T1:https://www.luogu.org/problem/T92604

规律没找出来,35pts打表滚粗

可以证明(我也不会),p=a^3+3a+1

在a=871时,p>2e9,所以枚举a,然后sqrt(L)判p是否是质数即可

T2:https://www.luogu.org/problem/T92605

考场上想了一想,觉得sum不好维护,于是就暴力了...

题目中给的y = -1或1,就保证了,对一个数进行修改,只会影响它前后一位的sum

做法:统计sum[i]:<=i的数的个数和,num[i],=i的数的个数和

然后对于一个数,它不可以删,当且仅当num[i] != 0 && sum[i] != i

记上面的答案为ok[i],线段树维护序列,于是我们获得了一个权值线段树,值为0/1

对于询问,相当于询问1-x的最靠右1的位置,轻松维护

对于修改,考虑几种情况就行

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<climits>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
#define O(x) cout << #x << " " << x << endl;
#define B cout << "breakpoint" << endl;
#define clr(a) memset(a,0,sizeof(a));
inline int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        (ans *= 10) += ch - '0';
        ch = getchar();
    }
    return ans * op;
}
const int maxn = 5e5 + 5;
int a[maxn],n,m,tp[maxn];
int num[maxn],sum[maxn];
bool ok[maxn];
struct node
{
    int sum,rm;
};
struct seg_tr
{
    node tr[maxn << 2];
    #define ls i << 1
    #define rs i << 1 | 1
    inline node up(node i,node l,node r)
    {
        i.sum = l.sum + r.sum;
        if(r.rm) i.rm = r.rm; else i.rm = l.rm;
        return i;
    }
    void build(int i,int l,int r)
    {
        if(l == r)
        {
            if(ok[l]) tr[i] = (node){1,l};
            else tr[i] = (node){0,0};
            return;
        }
        int mid = l + r >> 1;
        build(ls,l,mid);
        build(rs,mid + 1,r);
        tr[i] = up(tr[i],tr[ls],tr[rs]);
    }
    void change(int i,int l,int r,int x,int d)
    {
        if(l == r)
        {
            tr[i].sum = d;
            if(d) tr[i].rm = l;
            else tr[i].rm = 0;
            return;
        }
        int mid = l + r >> 1;
        if(x <= mid) change(ls,l,mid,x,d);
        else change(rs,mid + 1,r,x,d);
        tr[i] = up(tr[i],tr[ls],tr[rs]);
    }
    node query(int i,int l,int r,int ql,int qr)
    {
        if(l == ql && r == qr) return tr[i];
        int mid = l + r >> 1;
        if(qr <= mid) return query(ls,l,mid,ql,qr);
        else if(ql > mid) return query(rs,mid + 1,r,ql,qr);
        else
        {
            node tr1 = query(ls,l,mid,ql,mid),tr2 = query(rs,mid + 1,r,mid + 1,qr);
            node res = up(res,tr1,tr2);
            return res;
        }
    }
}T;
int main()
{
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    n = read(),m = read();
    for(int i = 1;i <= n;i++) a[i] = read(),num[a[i]]++;
    for(int i = 1;i <= n;i++) 
    {
        sum[i] = sum[i - 1] + num[i];
        if(sum[i] != i && num[i]) ok[i] = 1;
    }
    //for(int i = 1;i <= n;i++) printf("%d ",ok[i]);
    //cout << endl;
    T.build(1,1,n);
    while(m--)
    {
        int op = read();
        if(op == 1)
        {
            int x = read();
            node ans = T.query(1,1,n,1,x);
            if(!ans.sum) { printf("0
"); }
            else printf("%d
",sum[ans.rm]);
        }
        else
        {
            int x = read(),y = read();
            int p = a[x];
            a[x] += y;
            if(y == -1)
            {
                sum[p - 1]++;
                if(sum[p] - sum[p - 1] == 0) T.change(1,1,n,p,0);
                if(sum[p - 1] == p - 1) T.change(1,1,n,p - 1,0);
                else T.change(1,1,n,p - 1,1);
            }
            else
            {
                sum[p]--;
                if(sum[p] - sum[p - 1] == 0) T.change(1,1,n,p,0);
                else 
                {
                    if(sum[p] == p) T.change(1,1,n,p,0);
                    else T.change(1,1,n,p,1);
                }
                if(sum[p + 1] == p + 1) T.change(1,1,n,p + 1,0);
                else T.change(1,1,n,p + 1,1);
            }    
        }
    }
}
/*
6 7
2 3 4 6 6 6
1 6
2 4 -1
2 4 -1
1 6
1 4
2 2 -1
1 6
*/
     
View Code

T3:https://www.luogu.org/problem/T92606

考试的时候净做这题去了,结果树dp写的不好,一看这复杂度不爆炸?于是放弃

一开始的想法:f[u][i][j]:u及u的子树,分配i个1,号,j个2号的最多覆盖点

      那么先枚举u分配情况,再dp子树v,然后再给v分配水泵做dp,过程中临时数组记录

      第二问就二分,在分配给u水泵的时候使分配方案合法即可

这样O(T*n*200^3*logai),GG

然后看了标程恍然大悟

在处理u的时候,我们可以先处理v,更新f数组

在u的所有子树被处理完之后,最后再给u分配水泵

加上临时数组,也很好写

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<climits>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
#define O(x) cout << #x << " " << x << endl;
#define B cout << "breakpoint" << endl;
#define clr(a) memset(a,0,sizeof(a));
#define int long long
inline int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        (ans *= 10) += ch - '0';
        ch = getchar();
    }
    return ans * op;
}
const int maxn = 25;
const int M = 55;
struct egde
{
    int to,next;
}e[maxn << 1];
int fir[maxn],alloc;
void adde(int u,int v)
{
    e[++alloc].next = fir[u],fir[u] = alloc,e[alloc].to = v;
    swap(u,v);
    e[++alloc].next = fir[u],fir[u] = alloc,e[alloc].to = v;
}
int n,a[maxn],s1,s2,p1,p2;//p1 > p2
int f[25][M][M],tmp[M][M];
int mid,ans;
void dp(int u,int fa)
{
    for(int i = fir[u];i;i = e[i].next)
    {
        int v = e[i].to;
        if(v == fa) continue;
        dp(v,u);
        clr(tmp);
        for(int i = 0;i <= s1;i++)
            for(int j = 0;j <= s2;j++)
                for(int x = 0;x <= i;x++)
                    for(int y = 0;y <= j;y++)
                        tmp[i][j] = max(tmp[i][j],f[u][i - x][j - y] + f[v][x][y]);
        for(int i = 0;i <= s1;i++)
            for(int j = 0;j <= s2;j++)
                f[u][i][j] = tmp[i][j];
    }
    clr(tmp);
    for(int i = 0;i <= s1;i++)
        for(int j = 0;j <= s2;j++)
            for(int x = 0;x <= i;x++)
                for(int y = 0;y <= j;y++)
                if(x * p1 + y * p2 >= a[u] && x * p1 + y * p2 - a[u] <= mid)
                    tmp[i][j] = max(tmp[i][j],f[u][i - x][j - y] + 1);
    for(int i = 0;i <= s1;i++)
        for(int j = 0;j <= s2;j++)
            f[u][i][j] = tmp[i][j];
}
bool solve()
{
    clr(f);
    dp(1,0);
    int res = 0;
    for(int i = 0;i <= s1;i++)
        for(int j = 0;j <= s2;j++)
            res = max(res,f[1][i][j]);
    return res == ans;
}
main()
{
    freopen("system.in","r",stdin);
    freopen("system.out","w",stdout);
    int t = read();
    while(t--)
    {
        int n = read(); clr(fir); alloc = 0;
        for(int i = 1;i <= n;i++) a[i] = read();
        for(int i = 1;i < n;i++)
        {
            int u = read(),v = read();
            adde(u,v);
        }
        s1 = read(),s2 = read(),p1 = read(),p2 = read();
        mid = 1000000000;
        clr(f);
        dp(1,0);
        ans = 0;
        for(int i = 0;i <= s1;i++)
            for(int j = 0;j <= s2;j++)
                ans = max(ans,f[1][i][j]);        
        printf("%lld ",ans);
        int l = 0,r = 1000000000,prel = 0,prer = 0;
        int res = 0;
        while(l < r)
        {
            mid = (l + r) >> 1ll;
            if(solve()) r = mid,res = mid;
            else l = mid + 1;
            if(l == prel && r == prer) res = r,r--;
            prel = l,prer = r;
        }
        printf("%lld
",res);
    }
}
/*
1
4
5 30 20 20
1 2
1 3
3 4
4 1 10 15
*/
            
View Code
原文地址:https://www.cnblogs.com/LM-LBG/p/11329376.html