【Codeforces Round #317Div1 —— A】Lengthening Sticks【数学思维题】

    xiaoxiao2023-10-12  150

    题意:

    给出三个木棒的长度为 a a a b b b c c c,再给出一个长度 l l l 用来增加三根木棒的长度。三根木棒长度增加之和不能超过 l l l,可以为 0 0 0。问有多少种增长方案使得这三根木棒可以拼成一个三角形。 ( 1 ≤ a , b , c ≤ 3 ∗ 1 0 5 , 0 ≤ l ≤ 3 ∗ 1 0 5 ) (1\leq a,b,c\leq 3*10^5,0\leq l\leq 3*10^5) (1a,b,c3105,0l3105)


    思路:

    最简单的思路就是令 x x x y y y z z z分别为 a a a b b b c c c三根木棒的长度增加值。则 x + y + z ≤ l x+y+z \leq l x+y+zl,然后令 x + y + z = H  ( 0 ≤ H ≤ l ) x+y+z = \text{H} \ (0\leq H\leq l) x+y+z=H (0Hl),枚举 H \text{H} H,计算答案。

    比赛时我的思路是正向求解这个问题,然后就可以列出下述的式子。 { x + y + z = H a + x < b + y + z + c b + y < a + x + z + c z + c < a + x + b + y \left\{ \begin{aligned} & x+y+z = \text{H} \\ & a+x < b+y+z+c \\ & b+y < a+x+z+c \\ & z+c < a+x+b+y \\ \end{aligned} \right. x+y+z=Ha+x<b+y+z+cb+y<a+x+z+cz+c<a+x+b+y 于是问题就变成了一个线性规划问题,要求在线性规划区域中找到有多少个整点。然后就是不断的公式化简和推导,然后成功自闭…

    其实推导到这个程度之后就应该及时调整解题方向,线性规划问题的难度是很大的,现在也没有比较好的通用方向进行解决。所以推导到线性规划之后就应该及时调转车头。

    于是我们从反向入手,考虑一下容斥的思想。枚举 H \text{H} H之后,计算有多少种方案使得木棒无法构成三角形。木棒无法构成三角形主要是因为 最长的木棒长度 > > > 剩下两个木棒长度相加。因此我们枚举哪一根木棒是最长的木棒。

    假如枚举了木棒 a a a 为最长的木棒,则 { x + y + z = H a + x > b + y + c + z \left\{ \begin{aligned} & x+y+z = \text{H} \\ & a+x > b+y+c+z \\ \end{aligned} \right. {x+y+z=Ha+x>b+y+c+z 可以得到 2 ∗ x > b + c + H − a 2*x >b+c+\text{H}-a 2x>b+c+Ha,可以求出 x ≥ b a s e x\geq base xbase,于是只要给 a a a分配的长度大于 b a s e base base,就一定不可以构成三角形,于是问题转化为 x + y + z = H − b a s e x+y+z = \text{H}-base x+y+z=Hbase 有多少种分配方案。

    而所有的分配方案数为 x + y + z = H x+y+z = \text{H} x+y+z=H 的方案数。于是问题转化成了 a + b + c = z a+b+c = z a+b+c=z 有多少种不同的分配方案。

    首先考虑 b + c = z b+c=z b+c=z 有多少种不同的分配方案数。很明显有 z + 1 z+1 z+1 种分配方案,因此枚举 a a a,可以发现 a + b + c = z a+b+c=z a+b+c=z 的分配方案数为 ( z + 1 ) + z + ( z − 1 ) + . . . + 1 = ( z + 1 ) ∗ ( z + 2 ) 2 (z+1)+z+(z-1)+...+1 = \frac{(z+1)*(z+2)}{2} (z+1)+z+(z1)+...+1=2(z+1)(z+2)。当然也可以直接用组合数来求取答案,用隔板法可以得到总方案数为 C ( z + 2 , 2 ) C(z+2,2) C(z+2,2),至此本题即可解决。


    反思:

    首先总结一下常见的几个数学思维。 ① 正难则反 —— 容斥思想、反演思想 ② 变量思想 —— 设出未知量再不断进行方程化简,寻找规律

    再总结一下此类数学思维题的一些经验。 ① 一道题拿到手上之后,一定要对问题不断进行抽象化简,不断地深入思考,抽丝剥茧,逐层深入,才有可能能够解决这个问题。 ② 要擅长去找到规律,从小例子上找到思路然后放到大例子上进行解决。

    一个数学式子记录。 a + b + c = z , z a+b+c = z,z a+b+c=zz 为常数,一共有 C ( z + 2 , 2 ) C(z+2,2) C(z+2,2) 种 < a , b , c a,b,c a,b,c> 分配方案。


    代码:

    #include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <algorithm> #define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0) #define rep(i,a,b) for(int i = a; i <= b; i++) #define LOG1(x1,x2) cout << x1 << ": " << x2 << endl; #define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl; #define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl; typedef long long ll; typedef double db; const int N = 1e5+100; const int M = 1e5+100; const db EPS = 1e-9; using namespace std; int a,b,c,l; ll ans; ll calc(ll a1,ll len){ ll tp = ((a1+a1+len-1ll)*len)/2ll; return tp; } ll solve(int a1,int b1,int c1,int len){ ll tp = ceil((b1+c1+len-a1)/2.0-0.1); tp = max(0ll,tp); len -= tp; // LOG1("len",len); if(len < 0) return 0; ll hp = calc(1,len+1); return max(0ll,hp); } int main() { scanf("%d%d%d%d",&a,&b,&c,&l); rep(i,0,l){ ll tp1 = max(0ll,calc(1,i+1)); // LOG2("i",i,"tp1",tp1); ll tp2 = 0; tp2 += solve(a,b,c,i); // LOG1("tp2",tp2); tp2 += solve(b,a,c,i); // LOG1("tp2",tp2); tp2 += solve(c,a,b,i); // LOG1("tp2",tp2); ans += tp1-tp2; } printf("%lld\n",ans); return 0; }
    最新回复(0)