CF-959
Problem - C - Codeforces
dp+双指针
分析
要找到满足顺序执行操作后g值大于零的区间数。我们以以i为左端点考虑,对于和小于x的区间[i,j],对答案的贡献就是区间长度j-i,而对于第一个和大于x的区间[i,j],对答案贡献则是以j+1为左端点时的合法区间的数量
操作
对于每个端点i,我们可以预处理出第一个使得区间和大于x的点j,用f[i]表示以i为左端点的合法区间数,,转移方程则是$f[i]=f[p[i]+1]+p[i]-i$,也就得到了,最后对f[i]求和即可
const int N=2e5+5;
int f[N],p[N];
void solve() {
int n,x;cin>>n>>x;
vector<int>a(n+2);
rep(i,1,n) cin>>a[i];
int ans=0,j=0,now=0;
rep(i,1,n){
while(j<=n&&now<=x){
now+=a[++j];
}
p[i]=j;
now-=a[i];
// cout<<i<<" "<<j<<" "<<now<<endl;
}
f[n+1]=0,f[n+2]=0;
per(i,n,1){
f[i]=f[p[i]+1]+p[i]-i;
ans+=f[i];
// cout<<f[i]<<" \n"[i==1];
}
cout<<ans<<endl;
}
Problem - D - Codeforces
抽屉原理
常用于存在性的证明
n个物品放n+1个抽屉一定存在一个抽屉至少有两件物品
也包含有物品相同的情况
对于第x个操作,即以联通x条边,图中已有x+1个点,又所有数$modx$的值一定在[0,x-1]——有x种可能的值,所以x+1个点中一定存在两个点u、v满足$$u\equiv v$$(mod x),也即$x|(u-v)$
因为x是从1开始取的,所以以上的“已有”是成立的
注意只有倒着取x的值才能保证一定有解……
const int N=2e3+5;
int a[N],_,v[N];//,pre[N]
void solve() {
int n;cin>>n;
rep(i,1,n) cin>>a[i],v[i]=0;
vector<pair<int,int>>ans;
per(i,n-1,1){
// rep(k,0,i-1) pre[k]=-1;
vector<int>pre(i,-1);
rep(j,1,n){
// if(v[j]) continue;
if(v[j]==0&&pre[a[j]%i]!=-1){
ans.push_back({pre[a[j]%i],j});
v[j]=1;
break;
}
pre[a[j]%i]=j;
}
}
reverse(ans.begin(),ans.end());
cout<<"YES\n";
for(auto [x,y]:ans) cout<<x<<" "<<y<<endl;
}
Problem - E - Codeforces
最大或和
最终要把所有子树都删除,求这个过程中删除的子树大小的最大或和
对于一个大小为n的树,要得到n-1,我们可以先删1个子节点,再删除整个子树,同样,要得到n-2,可以先删两个子节点……也就是说我们可以得到[1,n]的所有值
也就是说只与每个子树的大小n有关……
设x为$[1,该子树的大小]$的任意值,于是问题转化为:求k个x值或和的最大值,显然可以对每个子树的大小拆位处理,对于第i位,如果能找到至少两个1,那么最优解是加上$2{i+1}-1$,否则若是只有一个1,显然保留该位,即加上$2i$最优
这部分可以自己手推一下……
int a[N],_;
void solve() {
int n,k,x;cin>>k;
int mx=0;
rep(i,1,k){
cin>>a[i];
rep(j,1,a[i]-1) cin>>x;
}
int ans=0;
per(i,31,0){
int cnt=0;
rep(j,1,k){
if(a[j]>>i&1) cnt++;
}
// cout<<i<<" "<<cnt<<endl;
if(cnt>=2){
ans+=(1ll<<(i+1))-1;
break;
}
else if(cnt==1) ans+=1ll<<i;
}
cout<<ans<<endl;
}
标签:959,pre,子树,int,rep,CF,cin,ans
From: https://www.cnblogs.com/mono-4/p/18313328