20250322比赛总结
反正是很唐,主要出在了答题策略上,误判了 T2 和 T4 的难度,而且 T2 没有二次拆贡献,导致方法极为复杂
类似情况其实可以果断放弃,其实问题跟 CSP 一模一样
T1 交错代码也是离谱
T1 光
https://www.gxyzoj.com/d/hzoj/p/4634
枚举对角线格子,然后可以记右上角的值为 \(x\), 左下角为 \(y\),除去枚举点这两个格子还需要 \(t1,t2\) 的贡献,另外两个格子还需要 \(p\) 的贡献,那么
可以发现下面两个式子的 \(x+y\) 是单峰的,可以二分,对于剩下的部分,因为 \(x,y\) 等价,所以前面的可以在找到最小的 \(x+y\) 后再去满足
主要是害怕自己的做法假,加了暴力,条件又没改
点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
int a,b,c,d,w,x,y,z;
int get(int mid)
{
int tmp1=x+mid,tmp2=y+mid/4;
return max(0,max(4*(b-tmp1),c-tmp2))+mid;
}
int main()
{
scanf("%d%d%d%d",&a,&b,&c,&d);
int ans=1e9;
for(int i=0;i<=a;i++)
{
for(int j=0;j<=d;j++)
{
w=i+j/4,x=i/2+j/2,y=i/2+j/2,z=j+i/4;
int l=0,r=b;
while(l<r)
{
// if(i==22&&j==0)printf("%d %d\n",l,r);
int mid=(l+r)>>1;
if(get(mid)<=get(mid+1)) r=mid;
else l=mid+1;
}
int t=get(l)-l,tmp=max(a-w,max(0,d-z));
// if(i==22&&j==0) printf("%d %d ",l,t);
if(l/2+t/2<tmp)
{
if(l%2) l++;
if(l/2+t/2<tmp&&t%2) t++;
if(l/2+t/2<tmp) l+=tmp*2;
}
ans=min(ans,l+t+i+j);
// if(l+t+i+j+tmp==638) printf("%d %d %d %d %d ",l,t,i,j,tmp);
}
}
printf("%d",ans);
return 0;
}
T2 爬
https://www.gxyzoj.com/d/hzoj/p/4635
显然拆位求贡献,其实每个节点的情况是互不干涉的,先看非根结点怎么求
如果当前位可移动到当前节点的权值中有 1,那么如果记有 k 个节点可能会到这里,方案数就是 \(2^{k-1}\),因为其他节点互不干涉,所以总方案数就是 \(2^{n-2}\)
对于根,因为他的权值不能动,所以如果这个位置是 1,而且儿子都是 0,那么方案数就是 \(2^{n-1}\)
然后减去只留下一个点的贡献,两种情况,一个是儿子和本身不动,二是父亲不为 1,移动到父亲,父亲的儿子不动
点击查看代码
#include<cstdio>
#define ll long long
using namespace std;
const int mod=1e9+7;
int n,edgenum,head[100005],d[100005];
int b[100005][35],p[35],f[100005];
ll qpow(ll x,int y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
struct edge{
int to,nxt;
}e[200005];
void add_edge(int u,int v)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
head[u]=edgenum;
}
void get(int id,int x)
{
for(int i=1;i<=32;i++)
{
b[id][i]=x&1;
x>>=1;
}
}
ll sum=0,a[100005];
void solve(int u)
{
for(int i=1;i<=32;i++) p[i]=b[u][i];
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
for(int j=1;j<=32;j++)
{
p[j]|=b[v][j];
}
}
for(int i=1;i<=32;i++)
{
if(p[i])
{
sum=(sum+qpow(2,n-2)*(1ll<<(i-1))%mod)%mod;
}
}
sum=(sum-qpow(2,n-2-d[u])*a[u]%mod+mod)%mod;
if(f[u]!=1)sum=(sum-qpow(2,n-2-d[f[u]])*a[u]%mod+mod)%mod;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
get(i,a[i]);
}
for(int i=2;i<=n;i++)
{
scanf("%d",&f[i]);
add_edge(f[i],i);
d[f[i]]++;
}
for(int i=2;i<=n;i++)
{
solve(i);
// printf("%d ",sum);
}
for(int i=1;i<=32;i++) p[i]=0;
for(int i=head[1];i;i=e[i].nxt)
{
int v=e[i].to;
for(int j=1;j<=32;j++)
{
p[j]|=b[v][j];
}
}
for(int i=1;i<=32;i++)
{
if(p[i]) sum=(sum+qpow(2,n-2)*(1ll<<(i-1))%mod)%mod;
if(b[1][i]&&!p[i]) sum=(sum+qpow(2,n-1)*(1ll<<(i-1))%mod)%mod;
}
sum=(sum-qpow(2,n-1-d[1])*a[1]%mod+mod)%mod;
printf("%lld",sum);
return 0;
}
T3 字符串
https://www.gxyzoj.com/d/hzoj/p/4636
可以先强制 B 开头,A 结尾,然后枚举段数,先放成 c 个 B + 1 个 A 的形式,此时,如果 A 有剩余,在开头放一个,B 有剩余,在结尾放一个
如果 A 还有剩余,那么每 \(a\) 个会有一个贡献
如果 B 还有剩余,现将所有段填成 \(kb+1\) 的形式,剩下的就是每 \(b\) 个一点贡献
点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
int T,n,m,a,b,c;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
int ans=1+(n-1)/a+(m-1)/b;
for(int i=1;i<=min(m/c,n);i++)
{
int sum=i*2-1;
if(n-i>0)
{
sum=sum+1+(n-i-1)/a;
}
if(c>b)
{
sum+=(c-1)/b*i;
}
if(m-c*i>0)
{
sum++;
int tmp=m-c*i-1;
int x=(b*((c+b-1)/b)+1)-c;
// printf("%d ",x);
if(1ll*x*i>=tmp) sum+=tmp/x;
else
{
sum+=i+(tmp-i*x)/b;
}
}
ans=max(ans,sum);
}
printf("%d\n",ans+1);
}
return 0;
}
T4 奇怪的函数
https://www.gxyzoj.com/d/hzoj/p/4637
性质1提示很明显,是分段函数,而且永远是三段
性质2的提示是函数可以拆分成任意段,然后得到每一段的操作方式,然后合并
所以可以分块处理,求出每个快的分段情况,然后带值即可
最后修改时暴力重构快即可
点击查看代码
#include<cstdio>
#include<algorithm>
#include<cmath>
#define int long long
using namespace std;
int n,a[100005],b[100005],blen,pos[100005],t;
int st[100005],ed[100005],q;
struct node{
int l,r,vl,vr,t;
}c[100005];
void get(int x)
{
int l=1,r=1e18,vl=1,vr=1e18,sum=0;
for(int i=st[x];i<=ed[x];i++)
{
if(a[i]==1) vl+=b[i],vr+=b[i],sum+=b[i];
if(a[i]==2)
{
if(vl>=b[i])
{
l=r+1;
break;
}
int y=b[i]-vl;
r=min(r,l+y),vr=min(vr,b[i]);
}
if(a[i]==3)
{
if(vr<=b[i])
{
l=r+1;
break;
}
int y=vr-b[i];
l=max(l,r-y),vl=max(vl,b[i]);
}
}
if(l>r)
{
int p=1;
for(int i=st[x];i<=ed[x];i++)
{
if(a[i]==1) p+=b[i];
if(a[i]==2) p=min(p,b[i]);
if(a[i]==3) p=max(p,b[i]);
}
c[x]=(node){-1,0,p,0,0};
return;
}
vl=l-1;
for(int i=st[x];i<=ed[x];i++)
{
if(a[i]==1) vl+=b[i];
if(a[i]==2) vl=min(vl,b[i]);
if(a[i]==3) vl=max(vl,b[i]);
}
vr=r+1;
for(int i=st[x];i<=ed[x];i++)
{
if(a[i]==1) vr+=b[i];
if(a[i]==2) vr=min(vr,b[i]);
if(a[i]==3) vr=max(vr,b[i]);
}
c[x]=(node){l,r,vl,vr,sum};
// printf("%lld %lld %lld %lld %lld\n",l,r,vl,vr,sum);
}
signed main()
{
// freopen("1.txt","r",stdin);
scanf("%lld",&n);
blen=sqrt(n);
t=n/blen;
if(n%blen) t++;
// printf("%d ",t);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&a[i],&b[i]);
}
for(int i=1;i<=t;i++)
{
st[i]=(i-1)*blen+1;
ed[i]=i*blen;
}
ed[t]=n;
for(int i=1;i<=t;i++)
{
for(int j=st[i];j<=ed[i];j++)
{
pos[j]=i;
}
get(i);
}
scanf("%lld",&q);
while(q--)
{
int opt,x,val;
scanf("%lld%lld",&opt,&x);
if(opt<=3)
{
scanf("%lld",&val);
a[x]=opt,b[x]=val;
get(pos[x]);
}
else
{
for(int i=1;i<=t;i++)
{
if(c[i].l==-1) x=c[i].vl;
else
{
if(x<c[i].l) x=c[i].vl;
else if(x>c[i].r) x=c[i].vr;
else x+=c[i].t;
}
}
printf("%lld\n",x);
}
}
return 0;
}

浙公网安备 33010602011771号