抱歉,抱歉,前两周忙着打网络赛,结果容斥专题断了整整两周。最后一个专题,总结性专题,现在继续。
第一题SGU-476
题意:3n个人,组成n个三人组,而且必须满足k组条件,每组条件表示a编号为a,b,c的三人不可以一个队。
分析:是容斥原理,用所有情况减去不能出现的情况,就是答案。对于不能出现的情况,对于条件的组数进行容斥,如果多组情况中有相同的成员编号那么就是不可能出现的,为0。这样的话,代码还是很好写的,而且用dfs的写法,还可以进行剪枝。
观察数据范围,发现答案很大,必须使用高精度。使用高精度打表的时候发现复杂度很高,预处理处理不出来,一个是高精度的模版不行(果断换掉),还有就是简化打表公式。对于没有条件的3n个人,方案数是 (3n)!6n∗n! ,知道通项,很方便的得到递推式 n0=1,ni=ni−1∗(3n−1)∗(3n−2)2 ,这样打表很显然快很多。
但是这样的话,对于极限情况还是很慢,因为进行了多次大数相加(大约为1000的复杂度),递归深度是20,那么总的复杂度就是 1000∗220=109 ,极限情况是会TLE的。
之后,我考虑对于每一组具有相同有效条件数的搜索,它的方案数和符号都是一样的,那么我可以先将他们累积起来,再直接进行一次大数乘法和一次大数加法。这样的话,将时间复杂度优化到了10^6(常数可能有点大),就可以过了。
#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define maxn 1000000+10
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define mp make_pair
#define FI first
#define SE second
#define IT iterator
#define PB push_back
#define Times 10
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double eps = 1e-10;
const double pi = acos(-1.0);
const ll mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const ll INF = (ll)1e18+300;
const double delta = 0.98;
inline void RI(int& x)
{
x=0;
char c=getchar();
while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
bool flag=1;
if(c=='-')
{
flag=0;
}
while(c<='9'&&c>='0')
{
x=x*10+c-'0';
c=getchar();
}
if(!flag)x=-x;
}
int compare(string str1,string str2)
{
if(str1.length()>str2.length()) return 1;
else if(str1.length()<str2.length()) return -1;
else return str1.compare(str2);
}
string add(string str1,string str2)
{
string str;
int len1=str1.length();
int len2=str2.length();
if(len1<len2)
{
for(int i=1;i<=len2-len1;i++)
str1="0"+str1;
}
else
{
for(int i=1;i<=len1-len2;i++)
str2="0"+str2;
}
len1=str1.length();
int cf=0;
int temp;
for(int i=len1-1;i>=0;i--)
{
temp=str1[i]-'0'+str2[i]-'0'+cf;
cf=temp/10;
temp%=10;
str=char(temp+'0')+str;
}
if(cf!=0) str=char(cf+'0')+str;
return str;
}
string sub(string str1,string str2)
{
string str;
int tmp=str1.length()-str2.length();
int cf=0;
for(int i=str2.length()-1;i>=0;i--)
{
if(str1[tmp+i]<str2[i]+cf)
{
str=char(str1[tmp+i]-str2[i]-cf+'0'+10)+str;
cf=1;
}
else
{
str=char(str1[tmp+i]-str2[i]-cf+'0')+str;
cf=0;
}
}
for(int i=tmp-1;i>=0;i--)
{
if(str1[i]-cf>='0')
{
str=char(str1[i]-cf)+str;
cf=0;
}
else
{
str=char(str1[i]-cf+10)+str;
cf=1;
}
}
str.erase(0,str.find_first_not_of('0'));
return str;
}
string mul(string str1,string str2)
{
string str;
int len1=str1.length();
int len2=str2.length();
string tempstr;
for(int i=len2-1;i>=0;i--)
{
tempstr="";
int temp=str2[i]-'0';
int t=0;
int cf=0;
if(temp!=0)
{
for(int j=1;j<=len2-1-i;j++)
tempstr+="0";
for(int j=len1-1;j>=0;j--)
{
t=(temp*(str1[j]-'0')+cf)%10;
cf=(temp*(str1[j]-'0')+cf)/10;
tempstr=char(t+'0')+tempstr;
}
if(cf!=0) tempstr=char(cf+'0')+tempstr;
}
str=add(str,tempstr);
}
str.erase(0,str.find_first_not_of('0'));
return str;
}
void div(string str1,string str2,string "ient,string &residue)
{
quotient=residue="";
if(str2=="0")
{
quotient=residue="ERROR";
return;
}
if(str1=="0")
{
quotient=residue="0";
return;
}
int res=compare(str1,str2);
if(res<0)
{
quotient="0";
residue=str1;
return;
}
else if(res==0)
{
quotient="1";
residue="0";
return;
}
else
{
int len1=str1.length();
int len2=str2.length();
string tempstr;
tempstr.append(str1,0,len2-1);
for(int i=len2-1;i<len1;i++)
{
tempstr=tempstr+str1[i];
tempstr.erase(0,tempstr.find_first_not_of('0'));
if(tempstr.empty())
tempstr="0";
for(char ch='9';ch>='0';ch--)
{
string str,tmp;
str=str+ch;
tmp=mul(str2,str);
if(compare(tmp,tempstr)<=0)
{
quotient=quotient+ch;
tempstr=sub(tempstr,tmp);
break;
}
}
}
residue=tempstr;
}
quotient.erase(0,quotient.find_first_not_of('0'));
if(quotient.empty()) quotient="0";
}
string dat[1000+10];
string ans,ant;
int tri[20+10][3];
int nun[20+10];
int vis[3000+10];
int n,k;
string Multiply(string s,int x)
{
reverse(s.begin(),s.end());
int cmp=0;
for(int i=0;i<s.size();i++)
{
cmp=(s[i]-'0')*x+cmp;
s[i]=(cmp%10+'0');
cmp/=10;
}
while(cmp)
{
s+=(cmp%10+'0');
cmp/=10;
}
reverse(s.begin(),s.end());
s.erase(0,s.find_first_not_of('0'));
return s;
}
void init(){
dat[0]="1";
for(int i=1;i<=1000;i++)
dat[i]=Multiply(dat[i-1],(3*i-2)*(3*i-1)/2);
}
void dfs(int len,int num){
if(len==k){
if(num)nun[num]++;
return ;
}
dfs(len+1,num);
for(int i=0;i<3;i++)
if(vis[tri[len][i]])return ;
for(int i=0;i<3;i++)
vis[tri[len][i]]=1;
dfs(len+1,num+1);
for(int i=0;i<3;i++)
vis[tri[len][i]]=0;
}
int main(){
init();
while(~scanf("%d %d",&n,&k)){
ans=dat[n];
ant="0";
for(int i=0;i<k;i++)
for(int j=0;j<3;j++)
scanf("%d",&tri[i][j]);
clr(vis,0);
clr(nun,0);
dfs(0,0);
for(int i=1;i<=k;i++)
if(nun[i]){
if(i&1)ant=add(ant,Multiply(dat[n-i],nun[i]));
else ans=add(ans,Multiply(dat[n-i],nun[i]));
}
string anw=sub(ans,ant);
if(anw.size())cout<<sub(ans,ant)<<endl;
else puts("0");
}
return 0;
}
第二题uva-10325
分析:容斥的简单题目,看前面的专题。某个区间中不能被集合连任何一个数整除的数的个数?
#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define maxn 300+10
#define SIZE 10000+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define mp make_pair
#define FI first
#define SE second
#define IT iterator
#define PB push_back
#define Times 10
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double eps = 1e-10;
const double pi = acos(-1.0);
const ll mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const ll INF = (ll)1e18+300;
const double delta = 0.98;
inline void RI(int& x)
{
x=0;
char c=getchar();
while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
bool flag=1;
if(c=='-')
{
flag=0;
}
while(c<='9'&&c>='0')
{
x=x*10+c-'0';
c=getchar();
}
if(!flag)x=-x;
}
int dat[20];
int n,m;
ll lcm(ll a,ll b){
return a/__gcd(a,b)*b;
}
ll solve(){
ll ans=0;
for(int i=1;i<(1<<m);i++){
ll t=1,num=0;
for(int j=0;j<m;j++)
if(i&(1<<j)){
t=lcm(t,dat[j]);
num++;
}
if(num&1)ans+=n/t;
else ans-=n/t;
}
return n-ans;
}
int main(){
while(~scanf("%d %d",&n,&m)){
for(int i=0;i<m;i++)
s