嘉然!为了你,我要写题解!Codeforces #738(Div.2)

嘉然!为了你,我要写题解!Codeforces Round #738(Div.2)

一个是刚复习完莫比乌斯来写了E题,顺便补了这场的D题(https://codeforces.com/contest/1559/problem/D2),感觉挺妙的,写个题解捏。

因为是赛后补题,有时间看题目背景,于是:

Mocha and Diana are friends in Zhijiang, both of them have a forest with nodes numbered from 1 to n, and they would like to add edges to their forests such that:

我直接补完所有题还写个题解

🤤🤤嘿嘿🤤🤤然然🤤🤤嘿嘿嘿🤤我的然然🤤🤤然然然然🤤🤤🤤我的然然

A. Mocha and Math

https://codeforces.com/contest/1559/problem/A

因为说了“This operation can be performed any number of times.”,那就好办了,只要我们想,我们总有办法让所有数变成\(a_1\&a_2 \&\dots \&a_n\),而AND运算一直做下去肯定不会让答案变大,只有可能变小,所以答案就是把所有\(a_i\)AND起来。

cin>>T;
rep(tc,1,T){
    cin>>n;rep(i,1,n)cin>>a[i];
    ll ret=a[1];
    rep(i,2,n)ret&=a[i];
    cout<<ret<<endl;
}

B.Mocha and Red and Blue

https://codeforces.com/contest/1559/problem/B

一个只含BR和?的序列,给?填上B或者R,使得BB或者RR尽可能少。换言之就是要尽可能多的BR,考虑每一段连续的X???X,其实中间问号怎么填是已经确定下来的:比如如果是B???...X,就一定是填成BRBR...X,就算?X后面变成RR也没办法,换成B开头也一样会少一个贡献。

所以其实如果从前往后扫一遍就能确定下来所有左边有东西的???段,然后我再判一下开头就是???X的情况:

char str[N],ch[]="BR";
vector<pii> seg;
void solve()
{
	seg.clear();
	for(int i=1;i<=n;i++)if(str[i]=='?')
	{
		int st=i;
		while(i+1<=n&&str[i+1]=='?')i++;
		seg.pb(mp(st,i));
	}
	for(auto itr:seg)
	{
		if(itr.fi==1)
		{
			for(int i=itr.se,t=(str[itr.se+1]=='B');i>=1;i--,t^=1)
				str[i]=ch[t];
		}else
		{
			for(int i=itr.fi,t=(str[itr.fi-1]=='B');i<=itr.se;i++,t^=1)
				str[i]=ch[t];
		}
	}
}
int main()
{
	scanf("%d",&T);
	rep(tc,1,T)
	{
		scanf("%d",&n);
		scanf("%s",str+1);
		solve();
		printf("%s\n",str+1);
	}
	return 0;
}

C. Mocha and Hiking

https://codeforces.com/contest/1559/problem/C

The city where Mocha lives in is called Zhijiang. There are n+1 villages and 2n−1 directed roads in this city.

🤤这下到枝江了捏。

画一下图,发现如果有01这种结构的出现,那就走\(1\to \dots \to i\to (n+1)\to i+1\to \dots n\)这条路线,否则一定是形如\(11\dots100\dots 00\)的结构,如果有1就从\((n+1)\)出发走一遍,否则全是0就从1开始走,最后走\(n+1\)。综上,符合条件的路径一定存在。

D. Mocha and Diana

https://codeforces.com/contest/1559/problem/D2

大的来了捏

Mocha and Diana are friends in Zhijiang, both of them have a forest with nodes numbered from 1 to n

, and they would like to add edges to their forests such that:

  • After adding edges, both of their graphs are still forests.
  • They add the same edges. That is, if an edge (u,v)

is added to Mocha's forest, then an edge (u,v)

  • is added to Diana's forest, and vice versa.

Mocha and Diana want to know the maximum number of edges they can add, and which edges to add.

D1和D2合起来写了,两张图,保证都是森林,要给\((u,v)\)加边就要同时加边,尽可能多地加一些边使得最后的图还是森林,给出加边的方案。类似的问题似乎之前在组队训练的时候见过一次(不过那题应该比较难的感觉),关键是注意到一些性质:首先说是森林,其实当连通块考虑就行,不用考虑树的具体形态。以及进一步地,连边也可以看成两个连通块连接。

这样这就引出了一个关键的信息:最后连完边一定有一张图只有一个连通块,否则就一定可以继续连边了对吧。进一步因为只有一个连通块,所以连边顺序也无所谓了,于是对于\(n\)比较小的D1题就可以n方地过掉了。

而对于D2,依然是用前面的性质往下想,既然连边顺序无所谓,那一开始就干脆全和1连:记两张图分别为\(G_0,G_1\)\(bl[x]\)\(x\)所在的连通块,对于\(2,3,\dots,n\)这些点,如果\(bl[i]\)\(G_0,G_1\)都和\(1\)不连通,那就直接连上\((1,i)\),否则如果\(bl[i]\)\(G_0\)和1连通,在\(G_1\)和1不连通,以及有一个\(bl[j]\)\(G_0\)和1不连通,而在\(G_1\)和1连通,那\(bl[i]\)\(bl[j]\)其实是可以连边的。

于是只要先把第一种情况的连起来,再\(O(n\log n)\)地处理一下剩下的点(因为是连通块,\(bl[i]\)有可能有重复的,去个重捏)

rep(i,2,n)
    if(find(0,1)!=find(0,i)&&find(1,1)!=find(1,i))
    {
        addEdge(0,1,i);addEdge(1,1,i);
        ret_edge.pb(mp(1,i));
    }
rep(i,2,n)
{
    if(find(0,1)!=find(0,i))s1.insert(find(0,i));
    if(find(1,1)!=find(1,i))s2.insert(find(1,i));
}
for(auto a=s1.begin(),b=s2.begin();a!=s1.end()&&b!=s2.end();a++,b++)
    ret_edge.pb(mp(*a,*b));
cout<<ret_edge.size()<<endl;
for(auto itr:ret_edge)
    cout<<itr.fi<<' '<<itr.se<<endl;

E. Mocha and Stars

https://codeforces.com/contest/1559/problem/E

如果没有\(gcd=1\)的约束就是一个比较裸的背包:\(f[i][j]\)表示容量\(j\)的背包装前\(i\)个物品的方案数,\(f[i][j]=\sum_{k=j-r[i]}^{j-l[i]}f[i-1][k]\)这样子,加上前缀和优化就可以\(O(nM)\)地求捏。

加上gcd这个约束之后,很容易让人想到莫比乌斯对吧(でしょう~)

先暴力地把答案写成

\(\begin{aligned}\sum_{a_1=l_1}^{r_1}\dots\sum_{a_n=l_n}^{r_n}[(a_1,\dots,a_n)=1][a_1+\dots+a_n\leq m]\\=\sum_{a_1=l_1}^{r_1}\dots\sum_{a_n}\sum_{d|(a_1,\dots,a_n)}\mu(d)[a_1+\dots+a_n\leq m]\end{aligned}\)

然后就是改写\(a_1,\dots,a_n\),把\(d\)放到前面:
\(\begin{aligned}\sum_{d=1}^m \mu(d) \sum_{a_1=\lceil l_1/d\rceil}^{\lfloor r_1/d \rfloor }\dots \sum_{a_n}[a_1+\dots+a_n\leq \frac{m}{d}]\end{aligned}\)

然后发现后面的问题变成了上面的背包,以及这个背包是\(O(n\frac{m}{d})\)的,调和级数,求和之后就是\(O(nM\log M)\)的。

于是就做完了捏~:

void init()
{
	mu[1]=1;
	rep(i,2,M-5)
	{
		if(!not_pri[i])
		{
			pri_list.pb(i);
			mu[i]=-1;
		}
		for(auto j:pri_list)
		{
			if(1ll*i*j>M-5)break;
			not_pri[i*j]=1;
			if(i%j==0){mu[i*j]=0;break;}
			mu[i*j]=-mu[i];
		}
	}
}
ll calc(int d,int mx)
{
	rep(i,0,mx)f[0][i]=1;
	rep(i,0,mx)s[0][i]=i+1; 
	rep(i,1,n)
	{
		int L=(l[i]+d-1)/d,R=r[i]/d;
		if(L>R)return 0;
		rep(j,0,mx)
		{
			f[i][j]=((j-L>=0?s[i-1][j-L]:0)-(j-R-1>=0?s[i-1][j-R-1]:0)+MOD)%MOD;
			s[i][j]=((j?s[i][j-1]:0)+f[i][j])%MOD;
		}
	}
	return f[n][mx];
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	init();
	cin>>n>>m;
	rep(i,1,n)cin>>l[i]>>r[i];
	ll ret=0;
	rep(d,1,m)ret=(ret+1ll*mu[d]*calc(d,m/d)%MOD+MOD)%MOD;
	cout<<ret;
	return 0;
}
posted @ 2021-09-17 23:45  yoshinow2001  阅读(177)  评论(4编辑  收藏  举报