绍一集训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 usum 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('
');
	}
	return 0;
}

然后光荣炸裂。

原文地址:https://www.cnblogs.com/cjjsb/p/9320700.html