1012练习赛

T1 果果系统 NKOJ8690

有一个n*m的网络,一个文件图标占用2*2的正方形空间,必须放在整格点之内,且不能超出桌面。
GOS2021 (果果操作系统(Guo Operating System,GOS)近期推出了2021船新版本)引入了一些船新忒星,例如允许图标重叠。
但是根据规定,如果一个格子存在多个重叠的图标,只会显示最后一个。
每个图标不可以被遮挡一半以上,而你想在GOS中放入尽可能多的图标。
问最大能放多少个,以及方案。

数据范围:
1<=n,m<=500

这道题稍微推一推就可以分析出方法,kzsn在这里主要说一下对于这种构造题需要注意的东西:

1.一定要看一下奇偶性,很有可能奇偶性不同会导致不同的结果!

2.不要局限于小样例或者自己造的小数据,小数据不能有些性质!

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define py pair<int,int>
vector<py>G;
signed main()
{
    int n, m;
    scanf("%d%d",&n,&m);
    if(n<2 || m<2) return puts("0"), 0;
    if(n % 2 == 0)
    {
        for(re i=1, z=0;i<=n-1;i+=2)
        {
            z^=1;
            if(z)
            {
                for(re j=1;j<=m-1;++j)
                G.push_back(py(i, j));
                if(i<=n-2)G.push_back(py(i+1, m-1));
            }
            else
            {
                for(re j=m-1;j;--j)
                G.push_back(py(i, j));
                if(i<=n-2)G.push_back(py(i+1, 1));
            }
        }
    }
    else if(m % 2 == 0)
    {
        for(re i=1, z=0;i<=m-1;i+=2)
        {
            z^=1;
            if(z)
            {
                for(re j=1;j<=n-1;++j)
                G.push_back(py(j, i));
                if(i<=m-2)G.push_back(py(n-1, i+1));
            }
            else
            {
                for(re j=n-1;j;--j)
                G.push_back(py(j, i));
                if(i<=m-2)G.push_back(py(1, i+1));
            }
        }
    }
    else
    {
        for(re i=1;i<=m-1;i+=2)G.push_back(py(n-1, i));
        for(re i=1, z=0;i<=n-1;i+=2)
        {
            z^=1;
            if(z)
            {
                for(re j=1;j<=m-1;++j)
                G.push_back(py(i, j));
                if(i<n-2)G.push_back(py(i+1, m-1));
            }
            else
            {
                for(re j=m-1;j;--j)
                G.push_back(py(i, j));
                if(i<n-2)G.push_back(py(i+1, 1));
            }
        }
    }
    printf("%d\n", (int)G.size());
    for(py i:G)printf("%d %d\n", i.first, i.second);
    return 0;
}
View Code

 

T2 质数幂 NKOJ8691

对于一个n>1的正整数n,对它进行质因数分解会得到一系列质因数 pi ,以及指数 ri 。求 min {r1,r2,r3,...,rk}。
多组数据。

数据范围:
1<=n<=1^18, 1<=T<=50000.

我们先想一下,如果不管多组数据,我们就单独求 n 的质因数分解,也不可能按照根号分解。

既然一个根号不行,那么我们试一下开立方根!

我们预处理处 1 ~ 100000 的质因数, 然后把很多个质因数分解 n ,分解完后 n 很有可能还剩余了很多质因数没被分解。

但是,那些质因数都是 大于 100000 的质因数, 所以只有可能是 质因数 x,y,

x2,x3,x4,(xy)2,只有这几种情况能让答案不为1。

至于怎么得到x和y呢,用系统函数pow即可大致得到(似乎有精度误差,所以我给它加减了个2和1)

但是这样搞的话,有多组数据,咋办啊!

由于我们耗得时间主要是在 1 ~ 100000 的质因数的个数,所以我们尝试将质因数个数减少。

减少到大约 6000 , 这样呢,复杂度就能过,质因数个数为 783 ,时间复杂度为O(783*T)

减少到 6000 后,我们分析也会变,但可以发现 6000 以上的质因数,它最多还是 x4,所以可过!

对了,可能有同学不理解那个(xy)2,这个东西能贡献的答案还是2,我们不需要将x,y分别求出来,只需要知道 n 能被开方就行了!

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define int long long
const int N=1e6+6, INF=1e18+1;
int lsp[N], prm[N], cnt;
inline void euler_phi(int x)
{
    for(re i=2;i<=x;++i){
        if(!lsp[i])prm[++cnt]=i;
        for(re j=1;j<=cnt && prm[j]*i<=x;++j)
        {
            lsp[prm[j]*i]=1;
            if(i%prm[j]==0)break;
        }
    }
}
inline int ksm(int x, int y){
    int ret=1;
    while(y--)ret=ret*x;
    return ret;
}
signed main()
{
    euler_phi(6000);
    int T, n;
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld",&n);
        int ret=100000;
        for(re i=1;i<=cnt && ret!=1 && prm[i]<=n;++i)
        if(n%prm[i]==0)
        {
            int tmp=0, t=prm[i];
            while(n%t==0) tmp++,n/=t;
            ret=min(ret,tmp);
        }
        if(n>1 && ret!=1)
        {
            for(re i=5;i>=1;--i)
            {
                if(i==1){ret=1;break;}
                int t=pow(1.0*n, 1.0/i), flag=0;
                if(t<6000)continue;
                for(re j=-2;j<=2;++j)
                if(ksm(t+j, i)==n)
                {
                    flag=1;
                    break;
                }
                if(flag)
                {
                    ret=min(ret, i);
                    break;
                }
            }
        }
        printf("%lld\n", ret);
    }
    return 0;
}
View Code

 

T3 又甘又刻 NKOJ8692

 

MikeZ同时在玩 n 款毒瘤手游,它们有的很肝,有的很氪,有的很凹。
MikeZ对于每个游戏都评价了三个值,肝度 a ,氪度 b,凹度 c。
定义一个游戏的相对可玩性为:
P = max{max(0, ai-a) + max(0, bi-b) + max(0, ci-c)}。
其中 i = 1 ~ n。
请你帮助 MikeZ 计算出相对可玩性最低的游戏。
若有多款游戏满足要求,选择编号最小的游戏
(编号小代表入坑早,凉得也更早)。

数据范围:
1<=n<=10^6, 1<=ai,bi,ci<=10^9

 这道题很巧妙的运用了max(0, x)的性质,也就是它不可能小于0!

我们想一种方法,比如说想把所有手游的 a,b,c 加起来,在相减得到的可玩度,肯定会小于等于真正的可玩度。

因为他的max(0),所以让权值和直接减会排除一些不太优秀的选择。

那么我们试一试把其中可能全部求一个最大值,也就是 $a,b,c,ab,ac,bc,abc$。

求出七个最大值之后呢,我们求第i个手游的可玩度时,可以直接得到是七种差值的最大值!

至于为什么呢,因为第i个手游的真正可玩度肯定是七种当中的一种,

而且由于max(0),所以真正的可玩度肯定是其中的最大值!

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define LL long long
const int N=1e6+6;
LL t1, t2, t3, t4, t5, t6, t7, p1, p2, p3, p4, p5, p6, p7, tmp=1e18, a[N], b[N], c[N], val;
int n, ret;
signed main()
{
    scanf("%d",&n);
    for(re i=1;i<=n;++i)
    {
        scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        t1=max(t1, a[i]);
        t2=max(t2, b[i]);
        t3=max(t3, c[i]);
        t4=max(t4, a[i]+b[i]);
        t5=max(t5, a[i]+c[i]);
        t6=max(t6, b[i]+c[i]);
        t7=max(t7, a[i]+b[i]+c[i]);
    }
    for(re i=1;i<=n;++i)
    {
        p1=t1-a[i];
        p2=t2-b[i];
        p3=t3-c[i];
        p4=t4-a[i]-b[i];
        p5=t5-a[i]-c[i];
        p6=t6-b[i]-c[i];
        p7=t7-a[i]-b[i]-c[i];
        val=max(p1, max(p2, max(p3, max(p4, max(p5, max(p6, p7))))));
        if(val<tmp)ret=i,tmp=val;
    }
    printf("%d %lld\n", ret, tmp);
    return 0;
}
View Code

 

T4 区间计数 NKOJ8693

对于序列 a1,a2,a3,...,an,求满足下列所有条件的区间 [l,r] 的个数。

1<=l<=r<=n;
区间内的数字各不相同
max{al,al+1,...,ar} - (r-l+1) <= k

数据范围:
1<=n,k<=3*10^5, 1<=ai<=n

可以很明显是笛卡尔树题!

因为区间内数字各不相同,所以我们可以 O(n) 求出对于 i 号点的左右边界!

然后就用笛卡尔树,找到一个数可以充当最大值的区间,就成笛卡尔树的板题了!

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define LL long long
const int N=3e5+5;

int n, K; LL ans;
int a[N], lt[N], rt[N], lst[N], Q[N];
int root, ls[N], rs[N], sz[N];
inline void DFS(int x)
{
    sz[x]=1;
    if(ls[x])DFS(ls[x]), sz[x]+=sz[ls[x]];
    if(rs[x])DFS(rs[x]), sz[x]+=sz[rs[x]];
    int ll=max(x-sz[ls[x]], lt[x]), rr=min(x+sz[rs[x]], rt[x]);
    int p=x-ll, q=rr-x;
    if(p<q)
    {
        for(re i=0;i<=p;++i)
        {
            int v=x-i;
            int fr=min(rt[v], rr);
            int to=x+(a[x]-K-1-i);
            to=max(to, x);
            if(fr-to+1>=1)ans+=fr-to+1;
        }
    }
    else
    {
        for(re i=0;i<=q;++i)
        {
            int v=x+i;
            int fr=max(lt[v], ll);
            int to=x-(a[x]-K-1-i);
            to=min(to, x);
            ans+=max(0,to-fr+1);
        }
    }
}
signed main()
{
    scanf("%d%d",&n,&K);
    for(re i=1, h=0;i<=n;++i)
    {
        scanf("%d",&a[i]);
        while(h && a[Q[h]]<=a[i])ls[i]=Q[h--];
        if(!h)root=i;
        else rs[Q[h]]=i;
        Q[++h]=i;
    }
    for(re i=1,fw=1;i<=n;++i)
    {
        lt[i]=fw;
        if(lst[a[i]])lt[i]=max(lt[i],lst[a[i]]+1);
        lst[a[i]]=i;
        fw=lt[i];
    }
    memset(lst, 0, sizeof lst);
    for(re i=n, fw=n;i;--i)
    {
        rt[i]=fw;
        if(lst[a[i]])rt[i]=min(rt[i], lst[a[i]]-1);
        lst[a[i]]=i;
        fw=rt[i];
    }
    DFS(root);
    printf("%lld\n", ans);
    return 0;
}
View Code

 

posted @ 2021-10-13 19:01  kzsn  阅读(87)  评论(0编辑  收藏  举报