1011练习赛


 1011练习赛

总结

 

T1倒是很简单几分钟就切了。
T2把考察内容搞错了,一开始就去想迭代加深了,饶了很大个弯。
重新想呢,把性质分析的很透彻,可惜没看出来怎么做。赛后一说是dp,瞬间明朗了。。。。。
T3,一道好难的dp,当时想出了“至少有一人给出的球为0”,但是又陷入了怎么去重的问题。。。最终就不太敢写了,只有一点暴力。
T4,这次kzsn做得很好,很努力地分析了样例,并且知道了这道题简化后的式子“求[i-t,i]的最大值,以及求和”。
不过就没更多进展了。

课下重写这些题时也有了些想法。
T1:居然TLE了,后来想想,像这样的暴力题,还是得用复杂度最好跑不满的方法,不然下次TLE就是在赛场上了。

T2:一定要注意数组大小,严格根据题目给出的大小来开数组,有时连3开到5都会MLE!!!切记。

T3:取模别嫌多,这样才不会WA。同时也收获了一点点小知识,像“平方和”这样需要除以6的式子,可以判断n%3的余数来直接除法,可以免一个逆元。

T4: 数据结构中有各种细节,尤其注意数组到负数。
不过T4写后感觉格外的好玩,因为它巧妙地用数学直线方程来做,以前没见过这种,挺有意思的。

总而言之,这次考的不太好,但是很好的把暴力分拿满了。。。(这好像也不算是优点)

 

 

 

T1:[模拟赛20211011]F NKOJ8686

给你两个数列ai,bi,你要找到一个排列p,使得每个a[i]^b[pi]=x都相等。
你要输出所有可能的x。

数据范围:
1<=n<=2000;0<=ai,bi<=10^9

  这道题可以各种做,map,离散化等。

  但是,像写这种题的时候,如果可能一定要写复杂度较优秀或者跑不满的方法,千万不要嫌麻烦!

(像我,之后在NKOJ上交的时候,TLE起飞)

 

#include<bits/stdc++.h>
using namespace std;
#define re register int
int n,LA,LB,a[2005],b[2005],c[2005];
int ans[4000006], ansnum;
int main()
{
    scanf("%d",&n);
    for(re i=1;i<=n;++i) scanf("%d",&a[i]);
    sort(a+1,a+n+1),LA=unique(a+1,a+n+1)-(a+1);
    for(re i=1;i<=n;++i) scanf("%d",&b[i]);
    sort(b+1,b+n+1),LB=unique(b+1,b+n+1)-(b+1);
    if(LA!=LB)return puts("0"),0;
    for(re i=1,x;i<=LA;++i)
    {
        x=a[1]^b[i];
        int flag=0;
        for(re j=1;j<=LA;++j)c[j]=a[j]^x;
        sort(c+1,c+LA+1);
        for(re j=1;j<=LA;++j)if(b[j]!=c[j]){flag=1;break;}
        if(!flag) ans[++ansnum]=x;
    }
    printf("%d\n",ansnum);
    for(re i=1;i<=ansnum;++i) printf("%d\n",ans[i]);
    return 0;
}
View Code

 

T2:[模拟赛20211011]S NKOJ8687

此处出锅,不能绝对值算贡献,得用逆序对,kzsn以后改!!!

有 n 个球,每个球有 RGY 三种颜色。
现在小 F 觉得如果有两个相邻的球颜色相同很丑。
他每次可以交换相邻两个球,问至少交换多少次才能不丑。

数据范围:n<=400

先想一想,如果我们知道最终这个序列长什么样,那么最少需要交换多少次呢?

在草稿纸上画一画可以知道,这样的答案就是 $ans = (sigma | 相应小球原有位置 - 当前位置 |) / 2$;

这里的相应小球指的是:如果这是最终序列的第$i$个‘A’,那么相应小球就是原有序列的第$i$个‘A’

所以我们可以预先处理出第$i$个‘A’的位置。

然后就可以dp了!

f[a][b][c][0/1/2]表示当前重构的序列已经有了a个R,b个G,c个Y,最后一位是RGY中的一个。

就会发现这个方程特别好转移!

f[a+1][b][c][0] = min(f[a][b][c][k!=0]),以此类瑞!

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=405, INF=0x3f3f3f3f;
char a[N];
inline int gt(char x){if(x=='R')return 0;if(x=='G')return 1;return 2;}
int f[N][N][N][3];
int G[N][N], num[3];
signed main()
{
    int n;
    scanf("%d",&n);
    scanf("%s",&a[1]);
    for(re i=1;i<=n;++i)
    {
        int t=gt(a[i]);
        G[t][num[t]]=i;
        num[t]++;
    }
    int na=num[0], nb=num[1], nc=num[2];
    memset(f, 0x3f, sizeof f);
    f[0][0][0][0]=f[0][0][0][1]=f[0][0][0][2]=0;
    for(re tot=0;tot<n;++tot)
    {
        for(re a=0;a<=tot && a<=na;++a)
        {
            for(re b=0;b<=tot && b<=nb && a+b<=tot;++b)
            {
                int c=tot-a-b;
                if(0<=c && c<=tot && c<=nc)
                {
                    for(re k=0;k<3;++k)
                    {
                        if(f[a][b][c][k]==INF)continue;
                        if(k!=0&&a<na)f[a+1][b][c][0]=min(f[a+1][b][c][0], f[a][b][c][k]+abs(a+b+c+1-G[0][a]));
                        if(k!=1&&b<nb)f[a][b+1][c][1]=min(f[a][b+1][c][1], f[a][b][c][k]+abs(a+b+c+1-G[1][b]));
                        if(k!=2&&c<nc)f[a][b][c+1][2]=min(f[a][b][c+1][2], f[a][b][c][k]+abs(a+b+c+1-G[2][c]));
                    }    
                }
            }
        }
    }
    int ans=min(f[na][nb][nc][0],min(f[na][nb][nc][1],f[na][nb][nc][2]));
    printf("%d", ans==INF?-1:ans/2);
    return 0;
}
View Code

 T3见这里 ,由于kzsn只是懂了,但不太能完美描述,所以。。。nkxjlym yyds

T4 :[模拟赛20211011]o NKOJ8689

有 n 个草堆堆,每个草堆堆燃起了 ai 亮度的火苗。
每个时刻,会有风从左往右吹,
对于每个 ai,会变成 max(ai-1,ai)(同时变)。
默认 a0 = 0。 
有 q 次询问,问 t 时刻,[l, r] 中火苗的亮度和。初始是 0 時刻。

这道题非常有意思!我维护了个直线的斜率和截距!

将ai随着时间t变化而变化的图像画出来可以知道(这里就参考其他人的博客了,kzsn不太会画图)

 

 以上是巨佬的题解,我就是这样做的,再稍加补充吧。

我们要求vi的前缀和,就可以用树状数组维护pi的前缀和以及qi的前缀和。

但由于pi和qi直接维护不太好维护,所以我又定义了个L[i]和R[i],表示图上的左右两边的斜率和截距。

这样的话,v[i]=R[i]-L[i],q和p就是vi的斜率截距。

至于在哪里需要将表达式更改,肯定是在L[i] “i==li+t+1”时更改,另一边R[i]一样的。

最终的答案就是ai的部分在内以及pi-1的前缀和+qi-1的前缀和*t。

总而言之,用树状数组维护斜率前缀和以及截距前缀和。

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define int long long

const int N=2e5+5;
int a[N], Q[N], l[N], r[N], lg[N], g[N][25], ans[N], n;
inline int ST(int x, int y){
    int s=lg[y-x+1];
    if(a[g[x][s]] > a[g[y-(1<<s)+1][s]])return g[x][s];
    else return g[y-(1<<s)+1][s];
}
struct BIT{
    int c[N];
    inline int lowbit(int x){return x&-x;}
    inline int getsum(int x){
        int ret=0;
        while(x>0)ret+=c[x],x-=lowbit(x);
        return ret;
    }
    inline void insert(int x,int d){
        while(x<=n)c[x]+=d,x+=lowbit(x);
    }
}B1, B2;
struct node{int k, b;}L[N], R[N], V[N];
struct cag{int type, id;node nw;};
vector<cag>G[N];
inline void update(int i){
    B1.insert(i, -V[i].k);
    B2.insert(i, -V[i].b);
    V[i]=(node){(R[i].k-L[i].k)*a[i], (R[i].b-L[i].b+1)*a[i]};
    B1.insert(i, V[i].k);
    B2.insert(i, V[i].b);
    if(V[i].k<0)
    {
        V[i].k=-V[i].k;
        int t=(V[i].b/V[i].k)+1;
        if(t)
        {
            G[t].push_back((cag){0, i, (node){0, 0}});
            G[t].push_back((cag){1, i, (node){0, -1}});
        }
        V[i].k=-V[i].k;
    }
}
inline int solve(int p, int t){
    int i=ST(max(1ll, p-t), p);
    return B2.getsum(i-1)+B1.getsum(i-1)*t+(p-(L[i].k*t+L[i].b)+1)*a[i];
}

struct qury{
    int t, l, r, id;
    inline bool operator<(const qury&p)const{
        return t<p.t;
    }
}qry[N];
signed main()
{
    int m;
    scanf("%lld%lld",&n,&m);
    for(re i=1;i<=n;++i){
        scanf("%lld",&a[i]);
        g[i][0]=i;
        if(i>1)lg[i]=lg[i>>1]+1;
    }
    for(re i=1;i<=20;++i)
        for(re j=1;j+(1<<i)-1<=n;++j)
            if(a[g[j][i-1]] > a[g[j+(1<<(i-1))][i-1]])g[j][i]=g[j][i-1];
                else g[j][i]=g[j+(1<<(i-1))][i-1];

    for(re i=1, h=0; i<=n; ++i){
        while(h && a[Q[h]]<=a[i])h--;
        l[i]=Q[h];
        Q[++h]=i;
    }
    for(re i=n, h=0; i; --i){
        while(h && a[Q[h]]<a[i])h--;
        r[i]=Q[h];if(!r[i])r[i]=n+1;
        Q[++h]=i;
    }
    for(re i=1;i<=n;++i){
        L[i]=(node){0,i};
        R[i]=(node){1,i};
        update(i);
        if(l[i])G[i-l[i]-1].push_back((cag){0, i, (node){1, l[i]+1}});//Äĸö¶Ëµã£¬¸üгÉʲôÑù
        G[r[i]-i-1].push_back((cag){1, i, (node){0, r[i]-1}});
    }
    for(re i=1;i<=m;++i)scanf("%lld%lld%lld",&qry[i].t,&qry[i].l,&qry[i].r), qry[i].id=i;
    sort(qry+1, qry+1+m);
    int t=-1;
    for(re i=1;i<=m;++i)
    {
        while(t<qry[i].t)
        {
            t++;
            for(cag x:G[t])
            {
                if(x.type==0)//ÐÞ¸Ä×ó¶Ëµã
                {
                    L[x.id]=x.nw;
                    update(x.id);
                }
                else
                {
                    R[x.id]=x.nw;
                    update(x.id);
                }
            }
        }
        ans[qry[i].id] = solve(qry[i].r, t)-solve(qry[i].l-1, t);
    }
    for(re i=1;i<=m;++i)printf("%lld\n", ans[i]);
    return 0;
}

特别有意思!!当然我发现有人居然只用了1000B就写完了这道题,那些巨佬太强了,而且看不懂。。。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM