Codeforces Round 906
由于水平有限,只能打 div2。
当天时间没打完就睡了,白兰白兰。
A
观察到同奇偶的位置数必须相等,简单分讨即可。
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
using namespace std;
const int N=2e5+60,M=2e6+20,mod=998244353;
int n,a[N],b[N];
void solve(){
cin>>n;
int tot=0;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
for(int i=1;i<n;i++) if(a[i]!=a[i+1]) tot++;
if(tot<2){
if(tot==0) return cout<<"Yes\n",void();
int cnt=0;
for(int i=1;i<=n;i++){
cnt+=(a[i]==a[1]);
}
if(cnt!=n/2&&cnt!=(n+1)/2) cout<<"No\n";
else cout<<"Yes\n";
}
else cout<<"No\n";
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
B
如果 \(s,t\) 都不是好串显然无解,而如果 \(t\) 不是好串那么插入 \(t\) 显然没用,若 \(t\) 是好串而首尾不同也没用,所以唯一只用考虑 \(t\) 首尾相同,随便判判即可。
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
using namespace std;
const int N=1e4+60,M=2e6+20,mod=998244353;
int n,m;char s[N],t[N];
void solve(){
cin>>n>>m;
cin>>s>>t;
bool fl=1;
int lst=-1;
for(int i=0;i<n-1;i++){
if(s[i]==s[i+1]){
if(lst!=s[i]-'0'&&lst!=-1) return cout<<"No\n",void();
lst=s[i]-'0';
}
}
if(lst==-1) return cout<<"Yes\n",void();
int can=-1;
for(int i=0;i<m-1;i++){
if(t[i]==t[i+1]) return cout<<"No\n",void();
}
if(t[0]==t[m-1]) can=t[0]-'0';
else return cout<<"No\n",void();
if(can!=lst) return cout<<"Yes\n",void();
else return cout<<"No\n",void();
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
C
考虑无解的必要条件,若 \(0,1\) 个数都不一样显然无解。那么猜测 \(0,1\) 个数相同必然有解。
确实必然有解,考虑现在左右匹配的 \(0,1\) 直接删除,直到串最左右 为 \(0\ \text{or} \ 1\),考虑在左右加 \(01\) 必然有匹配掉一对然后某边多加 \(0\ \text{or} \ 1\)。
例如 $0\dots 0 \rightarrow 100\dots 0 \rightarrow 00 \dots $ 即把其中右边的 \(0\) 扔到左边,\(1\) 同理会从左边扔到右边,固一直这样操作,最后一定变成 \(0\dots1\) 的形式,而又由于 \(0,1\) 个数相等,所以必然有解。
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
#define pb push_back
using namespace std;
const int N=1e4+60,M=2e6+20,mod=998244353;
int n;char s[N];
vector<int> ans;
void ins(int x){
ans.pb(x);
for(int i=n;i>x;i--) s[i+2]=s[i];
s[x+1]='0';s[x+2]='1';n+=2;
// cout<<s+1<<'\n';
}
int t[2];
void solve(){
cin>>n;cin>>(s+1);
ans.clear();
t[0]=t[1]=0;
for(int i=1;i<=n;i++) t[s[i]-'0']++;
if(t[0]!=t[1]) return cout<<-1<<'\n',void();
int l=1,r=n;
while(l<r){
if(s[l]!=s[r]) l++,r--;
else if(s[l]=='0'){
ins(r),l++,r++;
}
else ins(l-1),l++,r++;
}
cout<<(int)ans.size()<<'\n';
for(int v:ans) cout<<v<<' ';cout<<'\n';
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
D
这题赛时李超树写到一半发现有更加简单做法(我就说大家为啥都做这么快)
首先一个显然的事情,如果 \(i,j(j<i)\) 连边了,那么 \(\forall k \in [1,i-1]\) 都会和 \(i\) 连边,因为 \(i\) 的连通块 \(a\) 的和加上 \(j\) 的连通块 \(a\) 的和 \(\geq i\times j \times c\),那么 \(i\) 连通块 \(a\) 的和变成上述两者相加,而 \(k\in[1,i-1]\) 与 \(i\) 连边判断条件后者变小,前者变大显然满足。
那么考虑判断一个 \(i\) 前面时候存在 \(j<i\) 可以连向 \(i\),即 \(a_i+a_j \geq i \times j \times c\),通过移项 \(j(-ic)+a_j\geq -a_i\) 李超树维护即可,当然可以发现斜率上升,所以可以维护凸包栈,查询二分。
复杂度 \(O(n \log n)\)。
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
#define int ll
using namespace std;
const int N=2e5+60,M=2e6+20,mod=998244353,Inf=1e18;
struct Node{int k,b;}s[N];
int stk[N],pos[N],tp,n,c,a[N],nxt[N];
inline int get(int i,int j){return (s[i].b-s[j].b)/(s[i].k-s[j].k)+1;}
void solve(){
tp=0;cin>>n>>c;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) s[i].k=i,s[i].b=a[i];
for(int i=1;i<=n;i++) nxt[i]=i+1;
while(nxt[1]!=n+1){
int i=1;bool fl=0;
tp=0;
for(;i<=n;i=nxt[i]){
if(tp){
int v=-i*c;
int u=lower_bound(stk+1,stk+tp+1,v)-stk;
if((__int128_t)v*s[stk[u]].k+s[stk[u]].b>=-a[i]){fl=1;break;}
}
while(tp&&get(stk[tp],i)<=pos[tp]) tp--;
if(tp) pos[tp+1]=get(stk[tp],i);else pos[tp+1]=-Inf;
stk[++tp]=i;
}
if(!fl) break;
for(int j=nxt[1];j<=i;j++){
a[1]+=a[j];s[1].b+=a[j];
}
nxt[1]=i+1;
}
if(nxt[1]==n+1) cout<<"Yes\n";
else cout<<"No\n";
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;s[0].b=-Inf;
while(T--) solve();
return 0;
}
E1/E2
先讲讲 E1,设 \(s_i\) 表示 \(i\) 这个位置被覆盖次数,首先考虑两种贡献,一种是两个区间无交,这种直接选择区间 \(s_i=1\) 个数最大值和次大值,如果选出的两个区间有交,会被有交算的答案覆盖,不优。
而不难发现,若一个点 \(s_i=2\) 则有一对区间有交,所以有交区间对数 \(O(n)\)。
找出来暴力判断即可。
复杂度 \(O(n+m)\)。
E2,\(k\) 变大了,考虑通过 dp 解决。
考虑一种容易想的暴力,枚举所有 选择没被覆盖的点集,判断删除覆盖这个点集的区间数是否 \(leq k\)。
考虑怎么确定删除哪些区间的,考虑每个位置,考虑跨过上一个点的区间已经删除,设当前 \(i\)未被覆盖,上个未被覆盖位置为 \(j\),那么对于满足 \(j<l\leq i\leq r\) 的 \([l,r]\) 需要删除。
那么就可以 dp了,\(f_{i,p}\) 表示考虑 \([1,i]\) 且 \(i\) 未被覆盖,已经删除了 \(p\) 个区间,设 \(s_j=\sum\limits_{p=1}^{m} [j<l_p\leq i\leq r_p]\),有 \(f_{i,p} \leftarrow f_{j,p-s_j}\)。
这样是 \(O(n^2k)\),考虑优化。
注意 \(s_j>k\) 的没有意义,而随着 \(j\) 的增大,\(s_j\) 减小,那么可以算出 \(k\) 个分界点,然后用 st 表维护查询,st 表顺序插入是 \(O(\log n)\) 的。
复杂度 \(O(n k^2+n \log n k)\)。
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
#define pii pair<int,int>
#define MP make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;
const int N=2e5+20,M=1e6+20,mod=998244353,LGN=20,Inf=1e9;
int lg[N];
struct ST{
int a[N][LGN+2];
inline void ins(int p,int v){
a[p][0]=v;
for(int i=1;i<LGN;i++) a[p][i]=max(a[p][i-1],a[max(0,p-(1<<i-1))][i-1]);
}
inline int qry(int l,int r){
int k=lg[r-l+1];
return max(a[r][k],a[l+(1<<k)-1][k]);
}
}st[11];
int n,m,k,f[N][11],in[N];
vector<pii> s;
vector<int> out[N];
map<int,int> mp;
void solve(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++) in[i]=0,out[i].clear();
for(int i=0;i<=n;i++) for(int j=0;j<=k;j++) f[i][j]=-Inf;
f[0][0]=0;
for(int i=1,l,r;i<=m;i++){
cin>>l>>r;in[l]++,out[r].pb(l);
}
st[0].ins(0,0);
for(int i=1;i<=n;i++){
s.clear();
mp[i]+=in[i];
if(!mp[i]) mp.erase(i);
auto p=mp.end();
if(p==mp.begin()){
for(int j=0;j<=k;j++) f[i][j]=st[j].qry(0,i-1)+1;
}
else{
--p;int e=0;
for(;e<=k;){
if((*p).fi!=i) s.pb(MP((*p).fi,e));
e+=(*p).se;
if(p==mp.begin()) break;
--p;
}
int lst=i-1;
for(int q=0;q<(int)s.size();q++){
pii u=s[q];
// printf("%d %d %d\n",u.fi,i,u.se);
for(int j=u.se;j<=k;j++) f[i][j]=max(f[i][j],st[j-u.se].qry(u.fi,lst)+1);
lst=u.fi-1;
}
if(e<=k){
for(int j=e;j<=k;j++) f[i][j]=max(f[i][j],st[j-e].qry(0,lst)+1);
}
}
for(int j=0;j<=k;j++) st[j].ins(i,f[i][j]);
for(int v:out[i]) if(!--mp[v]) mp.erase(v);
}
for(int i=0;i<=k;i++) for(int j=0;j<=n;j++) for(int p=0;p<LGN;p++) st[i].a[j][k]=0;
int ans=0;
for(int i=0;i<=n;i++) ans=max(ans,f[i][k]);
cout<<ans<<'\n';
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
lg[0]=-1;for(int i=1;i<N;i++) lg[i]=lg[i>>1]+1;
int T;cin>>T;
while(T--) solve();
return 0;
}
F
sb 题,赛时没看。
首先考虑栈大小为 \(1\) 怎么做,连 \(i\rightarrow p_i\) 有环没用,会跑回自己。
所以删掉环,每个点的答案就是自己内向树的根。
多个点同理的,不断删环即可。
复杂度 \(O(n+ \sum k)\)。
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
using namespace std;
const int N=1e5+20,M=1e6+20,mod=998244353;
stack<int> s[N];
int n,ans[N],stk[N],tp;bool vis[N];
int dfs(int u){
if(ans[u]) return ans[u];
if(s[u].empty()) return u;
if(vis[u]){
stk[tp+1]=0;
while(stk[tp+1]!=u){
s[stk[tp]].pop();vis[stk[tp]]=0;tp--;
}return dfs(u);
}
stk[++tp]=u;vis[u]=1;
return dfs(s[u].top());
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1,k;i<=n;i++){
cin>>k;
for(int j=1,v;j<=k;j++) cin>>v,s[i].push(v);
}
for(int i=1;i<=n;i++){
tp=0;int res=dfs(i);
for(int j=1;j<=tp;j++) ans[stk[j]]=res;
cout<<res<<' ';
}
return 0;
}