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; }
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; }
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就写完了这道题,那些巨佬太强了,而且看不懂。。。