CSP 模拟 35
A 光
显然可以枚举三个数来求答案,如果枚举对角线的两个位置,我们会得到剩下两个位置的一组不等式,此时再枚举约数就可以消去向下取整的影响。
注意到不等式为两个直线,交点时剩下两个数的和最小(线性规划问题),求解交点。如果横坐标小于 \(0\) 就取 \(0\),如果纵坐标小于 \(0\) 就取 \(0\),如果不是整点,再查看一下左右两个整点那个最小即可。时间复杂度 \(\mathcal{O}(16n^2)\),也能枚举两个对角线的和,直接解就行,更简单一点。
另一种做法,显然要求最大的点分配最多,所以考虑每次给最大的点减 \(4\),保证取整,然后在每个点都小于一个阈值的时候暴力算,事实证明到 \(8\) 就行。
#include<bits/stdc++.h>
#define fo(i,s,t) for(int i=s;i<=t;++i)
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
signed main(){
freopen("light.in","r",stdin);freopen("light.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
int rA=read(),rB=read(),rC=read(),rD=read(),ans=6000,sum=rA+rB+rC+rD;
int A,B,C,D;
fo(a,0,2000)fo(d,0,2000){
A=rA-a-d/4;B=rB-a/2-d/2;C=rC-a/2-d/2;D=rD-a/4-d;
A=std::max(A,D);B=std::max(B,0);C=std::max(C,0);A=std::max(0,A);
fo(b,0,3)fo(c,0,3){
double x=(4*B-4*b-C+c)*1.0/15;
if(x<0)sum=std::ceil((C-c)*1.0/4)*4;
else{
if(-4*x+B-b<0){
sum=std::ceil((B-b)*1.0/4)*4;
}else{
double nx=std::floor(x);
x=std::ceil(x);
sum=std::ceil(x+-x/4.0+(C-c)/4.0)*4;
sum=std::min(sum,(int)std::ceil(-3*nx+B-b)*4);
}
}
sum+=b+c;sum=std::max(sum,2*A+b%2+c%2);
ans=std::min(ans,a+d+sum);
}
}
std::cout<<ans<<'\n';
}
B 爬
异或总和经典 trick,拆位贡献。单独考虑每一个节点上的贡献,每次只考虑一位,统计这一位上是 1 的节点数量(包括儿子和父亲),称为好点,否则是坏点,然后有一个组合式子 \(\sum_{i=1}^{n}{n\choose i}[2\nmid i]=2^{n-1}\),证明考虑分讨。然后这一位的贡献就等于奇数个好点上树方案乘上坏点上树方案再乘上没有关系的点的上树方案。对于节点一,特殊讨论一下即可。
C 字符串
放 T1 应该就有一车人切,贪心,考虑枚举 \(c\) 个 \(B\) 后接一个 \(A\) 的段数量 \(i\),首先会有切换的贡献 \(2i-1\) 和连续的贡献 \(\left\lfloor\frac{c-1}{b}\cdot i\right\rfloor\),然后分配 \(A\) 首先往开头放一个,然后在之前位置全放,统计贡献,分配 \(B\) 就先考虑将之前还没做出贡献的补完之后再放。
#include<bits/stdc++.h>
#define fo(i,s,t) for(int i=s;i<=t;++i)
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e5+10;
int n,m,a,b,c;
inline void sol(){
n=read(),m=read(),a=read(),b=read(),c=read();int sum=0,ans=0;
fo(i,0,m/c){
sum=i*2+(c-1)/b*i;
int _a=n-i,_b=m-i*c;
if(_a<0||_b<0)break;
if(_b){
_b--,sum++;
if(_b){
int w=std::min(i,_b/(b-(c-1)%b));
sum+=w+(_b-w*(b-(c-1)%b))/b;
}
}
if(_a){
_a--;sum++;
if(_a)sum+=_a/a;
}
ans=std::max(ans,sum);
}
std::cout<<ans<<'\n';
}
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
freopen("string.in", "r", stdin) , freopen("string.out", "w", stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
int T=read();
while(T--)sol();
}
D 奇怪的函数
简单手玩后发现这是一个不超过三段的分段函数(平升平),然后直接线段树上维护即可,我的写法很屎,就不贴代码了。
总结
打出退役水平了,思考完全不充分,见过的 trick 也没想起来,简单题也没看出来,贪心也想不到。四个小时没有一个小时的有效时间。暴力一打就挂,正解一点不会想,已经比刚学的时候还菜了。

浙公网安备 33010602011771号