11.13多校联训

T1 maware

Sol
显然的60分做法是直接维护二维前缀和,复杂度\(\mathcal{O}(n^4)\)
看范围像是\(\mathcal{O}(n^3\log n)\),很容易发现只有矩阵外的1个数能整除全部数个数的时候有可能有解。因数个数在随机情况下期望\(\log n\)个,所以可以枚举因数,然后枚举上下边界跑two-pointers就可以\(\mathcal{O}(n^3\log n)\)通过此题啦!
Code

#include<bits/stdc++.h>
using namespace std;
namespace io
{
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
        return f?x:-x;
    }
    inline void print(int x)
    {
        static int s[40],slen;slen=0;
        if(x==0){putchar('0');return;}
        if(x<0)x=-x,putchar('-');
        while(x){s[++slen]=x%10;x/=10;}
        for(int i=slen;i;i--)putchar('0'+s[i]);
        return;
    }
}
using namespace io;
const int maxn=210;
int sum[maxn][maxn];
int T,n,q[40010],mxt,s,ans;
int main()
{
    freopen("maware.in","r",stdin);
    freopen("maware.out","w",stdout);
    T=read();
    while(T--)
    {
        memset(q,0,sizeof(q));
        n=read();s=0;ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)sum[i][j]=sum[i][j-1]+read();
            s+=sum[i][n];
        }
        int cnt=0;
        for(int k=0;k<s;k++){
            if(k%(s-k))continue;
            int ret=0;
            for(int L=1;L<=n;L++)
            for(int R=L;R<=n;R++)
            {
                int l=0,r=0;
                for(int i=1;i<=n;i++)
                {
                    q[i]=q[i-1]+sum[i][R]-sum[i][L-1];
                    while(l<i&&q[l]+k<q[i])l++;
                    while(r<i&&q[r]+k<=q[i])r++;
                    if(q[l]+k>=q[i])ret+=r-l;
                }
            }
            if(ret)printf("%d %d\n",k/(s-k),ret);
        }
    }
    return 0;
}

T2 irori

我是傻逼,skip。

T3 yaya

Sol
为啥月球探测器会思考这种问题
题解的线段树确实没算明白。考场上写的前半段。
考虑先处理出初始的答案。然后每次修改暴力维护影响到的答案,查询直接\(\mathcal{O}(1)\)
修改一个数\(x\),它能影响到的点可以反向计算,也就是\(2x,2x+1,3x,3x+1,3x+2···,wx+w-2,wx+w-1\)。影响的点数量是\(\mathcal{O}(w^2)\)的。
有可能有多层影响,但是每次的\(x\)至少翻倍。而且按照dijkstra的思路,使用优先队列,每个点最多访问一次。最终单次修改复杂度上限我算出来是\(\mathcal{O}(\log ^2n*w^2)\)实际还远小于这个数。而且每次尝试都只有一个if,常数很小。稳过前60分,至于后面那25分怎么来的我不清楚,但是题解好像有证明复杂度上限更低,我没看懂。
Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=50010;
namespace io
{
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
        return f?x:-x;
    }
    inline void print(int x)
    {
        static int s[40],slen;slen=0;
        if(x==0){putchar('0');return;}
        if(x<0)x=-x,putchar('-');
        while(x){s[++slen]=x%10;x/=10;}
        for(int i=slen;i;i--)putchar('0'+s[i]);
        return;
    }
}
using namespace io;
int c[maxn];
int ans[maxn];
int n,w,q;
priority_queue<int,vector<int>,greater<int> >qu;
bool vis[maxn];
int main()
{
    freopen("yaya.in","r",stdin);
    freopen("yaya.out","w",stdout);
    n=read();w=read();q=read();
    for(int j=1;j<=n;j++)
    {
        int sq=sqrt(j);
        for(int i=1;i<=sq;i++)
        {
            if(j%i==0)c[j]+=2;
        }
        if(sq*sq==j)c[j]--;
        ans[j]=1000000000;
        for(int i=2;i<=w;i++)
        {
            ans[j]=min(ans[j],ans[j/i]);
        }
        ans[j]+=c[j];
    }
    while(q--)
    {
        int opt=read(),x=read();
        if(opt==1)
        {
            ans[x]--;c[x]--;
            vis[x]=1;qu.push(x);
            while(!qu.empty())
            {
                int now=qu.top();qu.pop();
                vis[now]=0;
                bool flag=0;
                for(int i=2;i<=w;i++)
                {
                    for(int j=0;j<i;j++)
                    {
                        int to=now*i+j;
                        if(to>n)
                        {
                            flag=1;break;
                        }
                        if(ans[now]+c[to]<ans[to])
                        {
                            ans[to]=ans[now]+c[to];
                            if(!vis[to])qu.push(to);
                        }
                    }
                    if(flag)break;
                }
            }
        }else
        {
            print(ans[x]);putchar('\n');
        }
    }
    return 0;
}

T4 komurasaki

我只会20分暴力加小剪枝。能把subtask2过掉一半已经很满意了。
暴力直接枚举当前点取什么数,然后更新后面的点的限制条件。
剪枝思路很简单:一个点如果根本没有边与它相连,那它随便取,可以减少递归层数。

posted @ 2021-11-14 18:40  wwlvv  阅读(43)  评论(0)    收藏  举报