绍一集训Round#1
到了之后看题,T1一看发现真熟悉,和之前做的一道题真的像,然后内心:
这里是绍一啊,不可能就出这么简单的题
我题意没理解错啊,这不是单独计算每条边的贡献么
维护一个人数的大小,然后直接搞一波就可以了吧
......(狂码5mins)
(一测样例,过了)
这么爽,赶紧写个暴力+对拍
......(15mins later)
居然过了,
瑟瑟发抖莫名自信
然后就真的A了T1不过真的很水
然后开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;
}
然后光荣炸裂。