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就寫完了這道題,那些巨佬太強了,而且看不懂。。。