• 绍一集训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;
    }
    

    然后光荣炸裂。

  • 相关阅读:
    double类型比大小的故事
    研究TR1中的东西方向啊方向
    About DWMAPI.DLL
    谈谈Windows程序中的字符编码
    [收藏]使用Microsoft Visual C++来检测和隔离内存泄漏
    浅谈文字编码和Unicode(下)
    【NYOJ】[169]素数
    [bbk5222] 第111集 第14章 数据库空间管理 00
    Redo Log
    平台移植项目案例 32bit windows9i > 64bit suse linux 10g
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9320700.html
Copyright © 2020-2023  润新知