到了以後看題,T1一看發現真熟悉,和以前作的一道題真的像,而後心裏:html
這裏是紹一啊,不可能就出這麼簡單的題git
我題意沒理解錯啊,這不是單獨計算每條邊的貢獻麼算法
維護一我的數的大小,而後直接搞一波就能夠了吧性能
......(狂碼5mins)spa
(一測樣例,過了)code
這麼爽,趕忙寫個暴力+對拍htm
......(15mins later)blog
竟然過了,
瑟瑟發抖莫名自信ci
而後就真的A了T1不過真的很水get
而後開T2,這種計算循環節的題目,lcm內的確定能夠作的啊
結果結合數據範圍直接寫了個暴力60pts的算法。
以後沒有什麼思路,感受再推下去也寫不出,而後就開始卡常
幾發以後開T3,發現這個式子是\(O(n^3)\)計算的,還要套上一個\(q\)感受要完
發現那個最大值很好求,直接RMQ以後\(O(1)\)求就能夠了,但\(O(qn^2)\)至於30pts
想一想怎麼\(O(qn)\),而後就開始畫圖,結果YY着:
枚舉每一個點爲最大值的狀況感受能夠
而後找出左右邊界的位置便可
這什麼鬼,不是單調棧水一水的東西嗎
(......立刻碼完調過樣例)
而後一算大概210左右,而後就開始拍T3
期間上廁所遇到了yekehe,跟他說基準分絕壁210,他也表示十分認同。
回來發現拍了10mins後T3WA了!
嚇得我一哆嗦,而後發現太大調不出,因此改了下數據範圍,而後一直沒拍出來。
後來感受是個很小的細節flag最後忐忑的交了上去。
結果最後一測170,T1A了,T2多卡了10分,但T3爆零了。
後來一看sol發現沒有考慮兩個數相同的狀況,我單調棧都寫了小於號,但實際上應該是一個地方加等於的。
而後發現yekehe也和我同樣T3爆零了。
最後看了下排名220已經很高了(最快的Rank2),這裏%%%一發X_o_r dalao220pts
前面已經講了不少了,這裏簡略的給出題解:
題目要求的是\(\sum u\sum v\ dis(u,v)\)。
咱們單獨考慮每一條邊對答案的貢獻。咱們DFS預處理出兩邊的人數,最後乘起來就行了。
由於這是一棵樹,而樹上路徑惟一。所以他們必然通過這條邊。
CODE
#include<cstdio> #include<cctype> #include<cstring> using namespace std; const int N=1000005,mod=1e9+7; struct edge { int to,next; }e[N<<1]; int head[N],n,x,y,cnt,num[N],tot,ans; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void double_add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; e[++cnt].to=x; e[cnt].next=head[y]; head[y]=cnt; } inline void inc(int &x,int y) { if ((x+=y)>=mod) x-=mod; } inline void DFS(int now,int fa) { register int i; inc(num[now],now); for (i=head[now];~i;i=e[i].next) if (e[i].to!=fa) DFS(e[i].to,now),inc(num[now],num[e[i].to]); } int main() { freopen("city.in","r",stdin); freopen("city.out","w",stdout); register int i; read(n); memset(head,-1,sizeof(head)); for (i=1;i<n;++i) read(x),read(y),double_add(x,y); DFS(1,-1); tot=1LL*(n+1)*n/2%mod; for (i=1;i<=n;++i) inc(ans,(1LL*num[i]*((tot-num[i]+mod)%mod))%mod); return printf("%d",ans),0; }
因爲我比較菜,到如今也不是很理解正解的玄學推導。
所以這裏推個dalao寫的blog:現場A掉此題的YPC dalao
而後給出個人抄來的CODE(寫法可能有細微區別)
#include<cstdio> #include<cctype> using namespace std; typedef long long LL; const LL N=500005; LL k,ans1,ans2,tot1,tot2,t,num[3],n,m,a[N],b[N],g; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(LL &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline LL gcd(LL n,LL m) { return m?gcd(m,n%m):n; } int main() { freopen("ai.in","r",stdin); freopen("ai.out","w",stdout); register LL i,j,s; read(n); read(m); read(k); for (i=1;i<=n;++i) read(a[i]); for (i=1;i<=m;++i)read(b[i]); g=gcd(n,m); for (i=1;i<=g;++i) { num[0]=num[1]=num[2]=0; for (j=i;j<=n;j+=g) ++num[a[j]]; for (j=i;j<=m;j+=g) ans1+=num[(b[j]+2)%3],ans2+=num[(b[j]+1)%3]; } t=n/g*m; ans1*=k/t; ans2*=k/t; k%=t; if (k) { for (s=1;s<=g;++s) { num[0]=num[1]=num[2]=0; LL tail=s; for (i=1;i<=(k-1)/n+1;++i) { ++num[b[tail]]; if (i<=(k-1)/n) tail=(tail+n-1)%m+1; } for (i=s;;i=(i+n-1)%m+1) { for (j=i;j<=n;j+=m) { if (j>(k-1)%n+1) --num[b[tail]]; ans1+=num[(a[j]+1)%3]; ans2+=num[(a[j]+2)%3]; if (j>(k-1)%n+1) ++num[b[tail]]; } tail=(tail+n-1)%m+1; ++num[b[tail]]; --num[b[i]]; if ((i+n-1)%m+1==s) break; } } } return printf("%lld %lld",ans1,ans2),0; }
仍是一道很套路的題,咱們考慮對原來的式子:
\(\sum_{L=l}^{r}\sum_{R=L}^{r}max(x_{i})(L<=i<=R)\)
咱們加一個RMQ就能夠\(O(1)\)查詢了。
而後考慮計算當一個點爲最大值是它兩邊最遠能擴展到哪裏。
這仍是先後兩遍單調棧就能夠解決的事情。
而後50pts到手。到後面的話因爲套路性太強,之後再來討論:
50~70ptsCODE(看機子的性能來決定分數)
#include<cstdio> #include<cctype> using namespace std; const int N=500005; int n,a[N],stack[N],top,front[N],back[N],num[N],q,l,r; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(long long x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline long long solve(int l,int r) { register int i; long long tot=0; for (i=l;i<=r;++i) { int L=front[i]<l?l-1:front[i],R=back[i]>r?r+1:back[i]; tot+=1LL*a[i]*(i-L)*(R-i); } return tot; } int main() { freopen("calc.in","r",stdin); freopen("calc.out","w",stdout); register int i; read(n); read(q); for (i=1;i<=n;++i) read(a[i]); for (i=1;i<=n;++i) { while (top&&stack[top]<=a[i]) --top; front[i]=num[top]; stack[++top]=a[i]; num[top]=i; } for (top=0,num[0]=n+1,i=n;i>=1;--i) { while (top&&stack[top]<a[i]) --top; back[i]=num[top]; stack[++top]=a[i]; num[top]=i; } while (q--) { read(l); read(r); write(solve(l,r)); putchar('\n'); } return 0; }
而後光榮炸裂。