#18搞OI不要会证明
Karen and Cards
设符合条件的三元组为 \((x,y,z)\)。枚举 \(x\),可以将 \(n\) 个三元组分为两类:\(a_i\ge x\) 和 \(a_i<x\)。对于 \(i\in [1,n],a_i\ge x\),需要满足的条件为 \(b_i<y\) 且 \(c_i<z\);对于 \(i\in [1,n],a_i<x\),需要满足的条件为 \(b_i<y\) 或者 \(c_i<z\)。
令 \(smx_y\) 表示 \(b_i\ge y\) 三元组中最大的 \(c_i\),\(mxc\) 表示 \(a_i\ge x\) 三元组中最大的 \(c_i\),\(z\) 的方案数为 \(r-\max(smax_j,mxc)\),因为对于 \(a_i\ge x\) 和 \(b_i\ge y\) 的三元组都需要满足 \(z>c_i\)。令 \(mxb\) 表示 \(a_i\ge x\) 的三元组中最大的 \(b_i\),枚举 \(y\in(mxb,q]\),对于每一个 \((x,y)\) 二元组计算答案。因为要求 \(mxb,mxc\),所以倒序枚举 \(x\),复杂度 \(O(n^2)\)。
考虑优化贡献过程,将 \(\max(smx_j,mxc)\) 拆开,因为 \(smx_j\) 不升,所以会存在一个 \(k\),当 \(j\ge k,smx_j\le mxc;j<k,smx_j>mxc\)。维护 \(smx_j\) 的前缀和即可。复杂度 \(O(n)\)。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
const int inf=1e9;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=5e5+10;
int n,p,q,r,mx[N],sum[N],ans;
vector<pii>d[N];
void solve(){
read(n),read(p),read(q),read(r);
for(int i=1;i<=n;i++){
int a,b,c;
read(a),read(b),read(c);
a=min(a,p);
b=min(b,q);
c=min(c,r);
d[a].pb(mp(b,c));
mx[b]=max(mx[b],c);
}
for(int i=q;i;i--){
mx[i]=max(mx[i+1],mx[i]);
}
for(int i=1;i<=q;i++){
sum[i]=sum[i-1]+mx[i];
}
int mxb=0,mxc=0,k=q+1;
for(int a=p;a;a--){
for(int i=0;i<d[a].size();i++){
int b=d[a][i].first,c=d[a][i].second;
mxb=max(mxb,b);
mxc=max(mxc,c);
while(k>mxb+1&&mx[k-1]<mxc){
k--;
}
}
k=max(k,mxb+1);
ans+=(r-mxc)*(q-k+1)+r*(k-mxb-1)-sum[k-1]+sum[mxb];
}
write_endl(ans);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
Foo Fighters
因为负数变正数和正数变负数是等价的,所以若所有数的和为负数,将所有数变为相反数。根据与的性质,会影响结果的只有小于权值为 \(1\) 的最高的位置,可以从小往大考虑每一位选或不选,如果当前位为最高位 \(1\) 的数的权值和为负数,则将所有当前位为 \(1\) 的数的权值取反。因为每一位贪心变小,所以最后结果一定符号会变。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
const int inf=1e9;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=3e5+10,Lg=62;
int n,val[N],s[N],mx[N];
int ans=0;
void solve(){
read(n);
for(int i=1;i<=n;i++){
read(val[i]),read(s[i]);
ans+=val[i];
for(int j=Lg;j>=0;j--){
if(s[i]>>j&1){
mx[i]=j;
break;
}
}
}
if(ans<0){
for(int i=1;i<=n;i++){
val[i]*=-1;
}
}
ans=0;
for(int i=0;i<=Lg;i++){
int res=0;
for(int j=1;j<=n;j++){
if((s[j]>>i&1ll)&&(mx[j]==i)){
res+=val[j];
}
}
if(res>0){
ans|=1ll<<i;
for(int j=1;j<=n;j++){
if(s[j]>>i&1ll){
val[j]*=-1;
}
}
}
}
write_endl(ans);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
Adam and Tree
对于一个点直接暴力往上跳的时候维护当前点到子树内所有叶子的最大值和次大值,如果更新到的点不能更新则不继续更新了。复杂度为 \(O(n\log n)\)
复杂度证明:假定 \(ans_i\) 表示最终的结果,那么更新次数为 \(\sum\limits_{i=1}^n ans_i\)。将树重链剖分后,并按照剖分结果染色,因为一个点经过的重链条数为 \(O(\log n)\),所以 \(ans_i\) 的级别是 \(O(\log n)\),复杂度 \(O(n\log n)\)。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
const int inf=1e9;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=1e6+10;
int n,fa[N],mx[N],maxi[N],now[N];
bool update(int pos){
if(mx[fa[pos]]<now[pos]){
maxi[fa[pos]]=mx[fa[pos]];
mx[fa[pos]]=now[pos];
}
else if(maxi[fa[pos]]<now[pos]){
maxi[fa[pos]]=now[pos];
}
int ans=max(mx[fa[pos]],maxi[fa[pos]]+1);
if(ans==now[fa[pos]]){
return 0;
}
now[fa[pos]]=ans;
return 1;
}
void solve(){
read(n);
for(int i=2;i<=n+1;i++){
read(fa[i]);
now[i]=1;
int pos=i;
while(fa[pos]){
if(!update(pos)){
break;
}
pos=fa[pos];
}
write_space(mx[1]);
}
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
Preorder Test
很容易想到换根dp,但实际上不用换根。
二分答案,将权值大于二分的答案的点权值赋为 \(1\),否则赋为 \(0\)。令 \(f_i\) 表示子树 \(i\) 内选择可以选到的最大的长度。转移 \(f_u=\sum\limits_{f_v=siz_v}f_v+mx_u\),其中 \(mx_u\) 表示 \(u\) 子树内最大的满足 \(f_v\not=siz_v\) 的 \(f_v\)。我们还可以在剩下的部分中选出一棵子树,连到当前的根,令其的 \(f\) 值为 \(nmx_u\),答案为 \(\max\{f_u+nmx_u\}\)。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
const int inf=1e9;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=2e5+10;
int n,a[N],k,val[N],flag[N],f[N],siz[N],ans,rt,rtt;
vector<int>e[N];
void dfs(int u,int fa,int x){
siz[u]=1;
int mx=0,maxi=0;
f[u]=1;
for(auto v:e[u]){
if(v==fa){
continue;
}
dfs(v,u,x);
siz[u]+=siz[v];
if(f[v]==siz[v]){
f[u]+=f[v];
continue;
}
if(mx<f[v]){
maxi=mx;
mx=f[v];
}
else if(maxi<f[v]){
maxi=f[v];
}
}
if(a[u]<val[x]){
f[u]=0;
}
else{
f[u]+=mx;
}
ans=max(ans,f[u]+maxi);
}
bool check(int x){
// ans=0;
// dfs(n,0,x);
// if(ans>=k){
// return 1;
// }
// ans=0;
// dfs(1,0,x);
// if(ans>=k){
// return 1;
// }
// ans=0;
// dfs(rt,0,x);
// if(ans>=k){
// return 1;
// }
ans=0;
dfs(rtt,0,x);
if(ans>=k){
return 1;
}
return 0;
}
void solve(){
read(n),read(k);
for(int i=1;i<=n;i++){
read(a[i]);
if(a[i]>a[rt]){
rt=i;
}
if(!rtt||a[i]<a[rtt]){
rtt=i;
}
val[i]=a[i];
}
for(int i=1;i<n;i++){
int u,v;
read(u),read(v);
e[u].pb(v);
e[v].pb(u);
}
sort(val+1,val+n+1);
int l=1,r=n,res=1;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
l=mid+1;
res=mid;
}
else{
r=mid-1;
}
}
write_endl(val[res]);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
因为未知原因,该份代码的根只能选到权值最小的点,选 \(1\) 会 wa on 25
,如果有知道什么原因的读者可以讲一下。
Maximum Subsequence Value
结论题,选三个是最优的。
证明:首先三个以下肯定不优于三个,此时答案仍为权值或,显然不优于三个。对于四个,令三个的答案为 \(ans\),若想答案不降,则每个答案的二进制位的 \(1\) 的个数至少为 \(2\),而增加一个数是怎么也不可能让一个位置 \(1\) 的个数增加 \(2\) 个,所以答案不增,更多同理。
枚举三个数,求三个数或的最大值。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
const int inf=1e9;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
int n,a[1010];
void solve(){
read(n);
int ans=0;
for(int i=1;i<=n;i++){
read(a[i]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
ans=max(ans,a[i]|a[j]|a[k]);
}
}
}
write_endl(ans);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
最小度限制生成树
令 \(val_u\) 表示 \((u,s)\) 这条边边权最小的边的边权,先忽略所有与 \(s\) 的连边,剩下部分肯定是求最小生成树。但是求最小生成树时,所有边都向 \(val_u\) 小的点合并。
加上与 \(s\) 的连边后,一个连通块中必须要至少与 \(s\) 连一条边,这条边的边权必然是连通块中所有点与 \(s\) 的连边中边权最小的。如果存在连通块无法连边或者连通块数大于 \(k\) 则无解。
增加 \(s\) 的度数,贪心考虑将一条边换为一条与 \(s\) 的相连的边,令 \(lst_u\) 表示 \(u\) 这个点在求最小生成树时合并的边的边权。最后优先修改 \(val_u-lst_u\) 小的点即可,正确性证明可以看 Alex_Wei的题解。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pdi pair<double,int>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define eps 1e-9
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=1e6+10,inf=1e18;
int n,m,fa[N],val[N],cnt,s,k;
int getfa(int x){
if(fa[x]!=x){
fa[x]=getfa(fa[x]);
}
return fa[x];
}
struct edge{
int u,v,w;
bool operator <(const edge &rhs)const{
return w<rhs.w;
}
}e[N];
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
read(n),read(m),read(s),read(k);
memset(val,0x7f,sizeof(val));
for(int i=1;i<=m;i++){
int u,v,w;
read(u),read(v),read(w);
if(u==s){
val[v]=min(val[v],w);
}
else if(v==s){
val[u]=min(val[u],w);
}
else{
e[++cnt]={u,v,w};
}
}
sort(e+1,e+cnt+1);
for(int i=1;i<=n;i++){
fa[i]=i;
}
vector<int>delta;
int ans=0;
for(int i=1;i<=cnt;i++){
int u=getfa(e[i].u),v=getfa(e[i].v);
if(u==v){
continue;
}
if(val[u]>val[v]){
swap(u,v);
}
fa[v]=u;
ans+=e[i].w;
if(val[v]<inf){
delta.pb(val[v]-e[i].w);
}
}
int deg=0;
for(int i=1;i<=n;i++){
if(fa[i]!=i||i==s){
continue;
}
deg++;
if(val[i]>inf){
puts("Impossible");
return 0;
}
ans+=val[i];
}
if(deg>k||deg+delta.size()<k){
puts("Impossible");
return 0;
}
sort(delta.begin(),delta.end());
for(int i=0;i<k-deg;i++){
ans+=delta[i];
}
write_endl(ans);
// cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
return 0;
}