嘉然!為了你,我要寫題解!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;
}