I
签。
有一个序列,每次你可以选择 恰 \(k\) 个数乘起来,并将这 \(k\) 个数删掉后替换成他们的乘积。
求最终序列最大值的最大可能值对 \(998244353\) 取模的结果。
\(n\le 2\times 10^5,k\le n,0\le V\le 10^9\)。
直接模拟就行,如果前 \(k\) 大有 \(0\) 就不乘。
有个技巧,先把所有非 \(0\) 值拿出来,设有 \(m\) 个。
我们每次消耗 \(k-1\) 个数字,所以只会做 \((m-1)/(k-1)\) 轮,于是只有 \(x=(m-1)\bmod (k-1)\) 个数字乘不上,于是扔掉前 \(x\) 小的就行了,这样非常好写。
C
小博弈。
两个绝顶聪明的人 \(A,B\) 博弈,给定 \([l,r]\) 的整数,令 \(x=l\),一开始 \(A\) 先手,重复进行以下操作:
当前回合的人从这些整数中取出一个 \(x\) 的倍数,取不出来就输了。
令 \(x\gets x+1\),交给另一个人。
求谁会赢。
首先发现偶数不会影响奇数。
其次必定有一个人只能取偶数,一个人只能取奇数,这根据 \(l\) 的奇偶性决定。
我们称一个人正常取当且仅当对于任意时刻,这个人取的数字都是 \(x\)。
这启发我们对 \(l,r\) 的奇偶性讨论:
-
\(l\) 奇 \(r\) 奇,说明 \(A\) 取奇数,且最后一次是 \(A\) 取,\(A\) 正常取就必胜。
-
\(l\) 偶 \(r\) 奇,说明 \(B\) 取奇数,且最后一次是 \(B\) 取,\(B\) 正常取就必胜。
-
\(l\) 奇 \(r\) 偶,说明 \(A\) 取奇数,需要让 \(B\) 在某个时刻提前取不到,只要 \(2l\le r\) 就可以办到。
-
\(l\) 偶 \(r\) 奇,说明 \(B\) 取奇数,要让 \(A\) 在某个时刻提前取不到,只要 \(2(l+1)\le r\) 就可以办到。
void solve() {cin>>l>>r;if((l&1)&&(r&1)) puts("Alice");if(!(l&1)&&(r&1)) puts("Bob");if((l&1)&&!(r&1)) {if(l*2<=r) puts("Alice");else puts("Bob");}if(!(l&1)&&!(r&1)) {if((l+1)*2<=r) puts("Bob");else puts("Alice");}
}
G
给定 \(n\) 条线段与 \(n\) 个横坐标,要将它们一一对应,得到的序列为它们的纵坐标,求序列的最大中位数。
\(n\le 10^5,V\le 10^{18}\),需求函数值不超过 \(10^{18}\)。
首先套路二分中位数,考虑check。
将 \(x\) 排序。
将所有线段 \(\ge mid\) 的范围的 \(x\) 坐标找出来并判断它在 \(x\) 序列上的范围。
问题转化成用 \(n\) 个点尽量多的覆盖 \(n\) 个区间。
贪心即可,结论是尽量让当前节点匹配。
\(O(n\log^2 V)\)。
bool check(int L) { tot=0;FOR(i,1,n) {if(a[i]*c[1]+b[i]<=a[i]*c[n]+b[i]) {int l=1,r=n,pos=n+1;while(l<=r) {int mid=l+r>>1;if(a[i]*c[mid]+b[i]>=L) r=mid-1,pos=mid;else l=mid+1; }if(pos<=n) s[++tot]={pos,n};} else {int l=1,r=n,pos=0;while(l<=r) {int mid=l+r>>1;if(a[i]*c[mid]+b[i]>=L) l=mid+1,pos=mid;else r=mid-1;}if(pos>=1) s[++tot]={1,pos};}}if(tot<(n+1)/2) return 0;sort(s+1,s+tot+1,cmp);int indx=0,nm=0;priority_queue<int>q;FOR(i,1,n) {while(indx+1<=tot&&s[indx+1].l==i) q.push(-s[indx+1].r),++indx;while(q.size()&&-q.top()<i) q.pop();if(q.size()) ++nm,q.pop();}return nm>=(n+1)/2;
}
B
给定一张简单无向图与排列 \(p\)。
你现在有个 dfs 函数,输出 dfs 序,并且他可以从任何联通块开始执行。
问至少加多少条边才能使得存在一种合法的 dfs 顺序使得输出的 dfs 序 \(=\) 排列 \(p\),输出方案。
\(|V|\le 3\times 10^5,|E|\le 5\times 10^5\)
首先通过将点重编号使得 \(p\) 变成 \(1,2,3,\dots ,n\)。
有一种显然的方法:
按新编号从小到大扫描联通块,如果没走过就走。
若当前点没有未走过的出点直接返回。
设 \(nw\) 表示当前已经输出完了 \(1\sim nw-1\),现在正要寻找 \(nw\),若当前出点有 \(nw\) 直接走。
否则从该点向 \(nw\) 连边。正确性显然。
实现上可以先对出边排序,接着不断扫描出边,如果遇到没走过的点就不断从当前点向 \(nw\) 连边跑。
复杂度 \(O(n\log n)\),瓶颈在于排序。
```plaintext
const int N=3e5+10;
int n,nw,p[N],m,id[N];
bitset<N>vis;
vector<pair<int,int> >pres,Ans;
vector<int>e[N];
void dfs(int x) {vis[x]=1;if(x==n) return ;for(int y:e[x]) {while(nw!=y&&!vis[y]) {Ans.eb(p[x],p[nw]);++nw;dfs(nw-1);}if(vis[y]) continue;++nw;dfs(y);}
}
main() {cin>>n>>m;FOR(i,1,m) {int u,v;cin>>u>>v;pres.eb(u,v);}FOR(i,1,n) p[i]=read(),id[p[i]]=i;for(auto q:pres) e[id[q.fr]].eb(id[q.se]),e[id[q.se]].eb(id[q.fr]);FOR(i,1,n) sort(e[i].begin(),e[i].end());nw=1;FOR(i,1,n) if(!vis[i]&&nw==i) ++nw,dfs(i);cout<<Ans.size()<<'\n';for(auto q:Ans) cout<<q.fr<<" "<<q.se<<"\n";return 0;
}
D
给你一个串 \(S\),若 \(S_i=1\) 说明 \(i\) 位置放了 \(10^{18}\) 个石子,否则啥都没放。
你可以选择第 \(1\le i\le n-2\) 堆取出若干个石子,并将 \(i+1,i+2\) 两堆互换。
问是否可以拿完所有石子。
\(|S|\le 10^6\)
考虑对后 \(5\) 个进行分讨。
- \(S_n=S_{n-1}=0\) 有解。
- \(S_{n-2}+S_{n-3}+S_{n-4}\ge 2\) 有解。
- \(S_{n-3}=S_{n-1}=1\) 有解。
设 \(check(l,r)\) 表示 \([l,r]\) 是否可以全部为 \(1\)。
- \(S_r=1\),\(check(l,r)=check(l,r-1)\)
- 否则 \(check(l,r)=check(l-2,r-1)\)。
边界是 \(l\le 0 和 l>r\)。
复杂度线性。
(((a[n-2]=='1'||a[n-3]=='1')&&check(n-4,n-4))|
(a[n]=='0'&&a[n-1]=='0')|
(a[n-1]=='1'&&check(n-3,n-3))|
(check(n-3,n-2))|
(check(n-4,n-3)))?puts("Yes"):puts("No");
E
给定一张联通简单无向图,判断图中所有简单环是否长度相等。
\(|V|,|E|\le 5\times 10^5\)。
求出 vdcc,首先判断单个点双。
发现点双只可能是:孤点、两点一线、单环、只存在两个度数 \(>2\) 的点且将这两个点以及相连的边删掉后剩余若干条链且一开始没有这两个点直接相连的边。
单独对于每个点双是好判断的,求出每个边属于的点双编号就做完了。
但一个点可能属于多个点双,暴力做需要集合求交,怎么办?
引理-已知点双求边集
在正常的、vdcc编号从小到大的 tarjan 算法中,若 \(id_x\) 表示 \(x\) 所在点双中编号最大的点双的编号,则一条边 \((u,v)\) 所在的点双编号是 \(\min(id_u,id_v)\)。
证明见此贴
复杂度 \(O(n)\)。
int n,m,dfn[N],siz[N],low[N],st[N],id[N],deg[N],fa[N],tot,top,tme;
pair<int,int>E[N];
vector<int>vdcc[N],e[N];
vector<pair<int,int> >vd[N];
int Get(int x) {if(x==fa[x]) return x;return fa[x]=Get(fa[x]);
}
void Merge(int x,int y) {siz[Get(x)]+=siz[Get(y)];fa[Get(y)]=Get(x);
}
void Clear() {FOR(i,1,tot) vd[i].clear(),vdcc[i].clear();FOR(i,1,n) e[i].clear(),dfn[i]=low[i]=id[i]=deg[i]=st[i]=0;tot=top=tme=0;
}
void dfs(int x) {dfn[st[++top]=x]=low[x]=++tme;for(int y:e[x]) {if(!dfn[y]) {dfs(y);cmin(low[x],low[y]);if(low[y]>=dfn[x]) { ++tot;while(st[top+1]!=y) vdcc[tot].eb(st[top]),id[st[top]]=tot,--top;vdcc[tot].eb(x);id[x]=tot;}} else cmin(low[x],dfn[y]);}
}
void solve() {Clear();cin>>n>>m;FOR(i,1,m) {int u=read(),v=read();E[i]={u,v};e[u].eb(v),e[v].eb(u);}FOR(i,1,n) if(!dfn[i]) dfs(i);FOR(i,1,m) {int u=E[i].fr,v=E[i].se;vd[min(id[u],id[v])].eb(u,v);}vector<int>vec;FOR(i,1,tot) {for(int x:vdcc[i]) fa[x]=x,deg[x]=0,siz[x]=1;for(auto p:vd[i]) {int u=p.fr,v=p.se;deg[u]++,deg[v]++;}int cnt=0,U=0,V=0;for(int x:vdcc[i]) {if(deg[x]>2) {++cnt;if(!U) U=x;else V=x;}}int Sz=deg[U];if(!cnt) {if(vdcc[i].size()<=2) continue;vec.eb(vdcc[i].size());continue;}if(cnt>2) {puts("No");return ;}for(auto p:vd[i]) {int u=p.fr,v=p.se;if(u==U||v==U||u==V||v==V) continue;if(Get(u)!=Get(v)) Merge(u,v);}vector<int>ed;for(auto x:vdcc[i]) {if(x==U||x==V||Get(x)!=x) continue;ed.eb(siz[x]);}if(ed.size()!=Sz) {puts("No");return ;}sort(ed.begin(),ed.end());ed.erase(unique(ed.begin(),ed.end()),ed.end());if(ed.size()>1) {puts("No");return ;}int a=(ed[0]+1)*2;vec.eb(a);}sort(vec.begin(),vec.end());vec.erase(unique(vec.begin(),vec.end()),vec.end());if(vec.size()>1) {puts("No");return ;}puts("Yes");
}