ACM竞赛中的逆向思维

Posted SuPhoebe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ACM竞赛中的逆向思维相关的知识,希望对你有一定的参考价值。

在竞赛过程中,尤其是近期训练,遇到了不少一定要用逆向思维才能解决的题目。
为此做一系列的总结。希望能够对大家有所帮助。
同时,我也会做成PPT,供14级训练使用。

其中有部分问题摘自于2005年国家集训队唐文斌的《正难则反–浅谈逆向思维在解题中的应用》论文。

容斥方面

逆向思维在容斥方面的应用相当广泛,也可以说容斥就是逆向思维的一种体现。

HDU 5072 Coprime 同色三角形

题目大意:

给了 n 个不同的数,要求有多少个三元组,两两互质 或者 两两不互质

思路:

原形是同色三角形问题。
总的三角形的个数是C(n,3),只需减去不同色的三角形即可。这就是逆向思维。
对于每个点(数),与它互质的连红边,不互质的连蓝边,那么对于该点不同色三角形个数为2。除以 2 的原因是,对于同一个三角形,我们枚举点的时候被计算了两次。
那么同色三角形个数为C3n2

问题就变成了:
如何求 原来序列里面的n个数跟某个数k不互质的个数(互质的就是 nk 了)?

可以将原来的 n 个数,每一个都把他们的不同的质因数都求出来,然后枚举它们能够组合的数1<<cnt,用一个数组 num 记录,每枚举到一个数,那么数组对应的就 +1

对于数 k ,也把它的不同质因数求出来,同样枚举它能够组合的所有数t,然后奇加偶减 num

#include<bits/stdc++.h>
typedef long long ll;
const int N = 200005;

int p[N][15], vis[N], a[N], num[N];
int n;
void Prime()

    memset(vis, 0, sizeof vis);
    for(int i = 0; i < N; ++i) p[i][0] = 0;
    for(int i = 2; i < N; ++i) if(!vis[i])
        
            p[i][ ++p[i][0] ] = i;
            for(int j = i + i; j < N; j += i)
            
                vis[j] = 1;
                p[j][ ++p[j][0] ] = i;
            
        
    p[0][ ++p[0][0] ] = 1;    //考虑0的情况


void init()

    memset(num, 0, sizeof num);
    for(int k = 0; k < n; ++k)
    
        int now = a[k];
        int cnt = p[ now ][0];
        for(int i = 1; i < (1 << cnt); ++i)
        
            int t = 1;
            for(int j = 0; j < cnt; ++j) if((1 << j) & i)
                
                    t *= p[ now ][j + 1];
                
            num[t]++;
        
    


void solve()

    ll ans = 0, res, sum = 0;
    ans = (ll)n * (n - 1) * (n - 2) / 6;
    int tot = 0;
    for(int k = 0; k < n; ++k)
    
        int now = a[k];
        int cnt = p[now][0];
        res = 0;
        for(int i = 1; i < (1 << cnt); ++i)
        
            int t = 1, g = 0;
            for(int j = 0; j < cnt; ++j) if((1 << j) & i)
                
                    t *= p[ now ][j + 1];
                    g++;
                
            if(g & 1)  res += num[t];
            else       res -= num[t];
        

        if(res == 0) continue;
        sum += (res - 1) * (n - res);
    
    printf("%lld\\n", ans  - sum / 2);


int main()

    int T;
    scanf("%d", &T);
    Prime();
    while(T --)
    
        scanf("%d", &n);
        for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
        init();
        solve();
    

ZOJ 1442 Dinner is Ready 不等式解集

此问题的思路来源于唐文斌论文。

题目大意:

妈妈烧了 M 根骨头分给n个孩子们,第 i 个孩子有两个参数Mini Maxi ,分别表示这个孩子至少要得到 Mini 根骨头,至多得到 Maxi 根骨头。
输出一个整数,表示妈妈有多少种分配方案(骨头不能浪费,必须都分给孩子们)。

思路:

这题的模型确实很简单,即求如下方程组的整数解个数。

i=1nXi=MMin1X1Max1Min2X2Max2MinnXnMaxn

我们也知道,方程组简单形式

i=1nXi=MXi0 的整数解个数是 C[简单思维题]Sequence(山东省第九届ACM大学生程序设计竞赛E题)

第九届“图灵杯”NEUQ-ACM程序设计竞赛个人题解

第九届“图灵杯”NEUQ-ACM程序设计竞赛个人题解

好题第九届“图灵杯”NEUQ-ACM程序设计竞赛个人赛 F-第二大数 思维

好题第九届“图灵杯”NEUQ-ACM程序设计竞赛个人赛 G-Num 思维+推公式

hdu-2955(01背包+逆向思维+审题)