ACM竞赛中的逆向思维
Posted SuPhoebe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ACM竞赛中的逆向思维相关的知识,希望对你有一定的参考价值。
在竞赛过程中,尤其是近期训练,遇到了不少一定要用逆向思维才能解决的题目。
为此做一系列的总结。希望能够对大家有所帮助。
同时,我也会做成PPT,供14级训练使用。
其中有部分问题摘自于2005年国家集训队唐文斌的《正难则反–浅谈逆向思维在解题中的应用》论文。
容斥方面
逆向思维在容斥方面的应用相当广泛,也可以说容斥就是逆向思维的一种体现。
HDU 5072 Coprime 同色三角形
题目大意:
给了 n 个不同的数,要求有多少个三元组,两两互质 或者 两两不互质。
思路:
原形是同色三角形问题。
总的三角形的个数是C(n,3),只需减去不同色的三角形即可。这就是逆向思维。
对于每个点(数),与它互质的连红边,不互质的连蓝边,那么对于该点不同色三角形个数为
那么同色三角形个数为
问题就变成了:
如何求 原来序列里面的n个数跟某个数k不互质的个数(互质的就是
n−k
了)?
可以将原来的
n
个数,每一个都把他们的不同的质因数都求出来,然后枚举它们能够组合的数
对于数
k
,也把它的不同质因数求出来,同样枚举它能够组合的所有数
#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
根骨头分给
输出一个整数,表示妈妈有多少种分配方案(骨头不能浪费,必须都分给孩子们)。
思路:
这题的模型确实很简单,即求如下方程组的整数解个数。
我们也知道,方程组简单形式
⎧⎩⎨⎪⎪⎪⎪∑i=1nXi=MXi≥0 的整数解个数是 C[简单思维题]Sequence(山东省第九届ACM大学生程序设计竞赛E题)好题第九届“图灵杯”NEUQ-ACM程序设计竞赛个人赛 F-第二大数 思维