题意简述
一个长度为 的数列 ,每个数都在 之间,有 个限制。第 个限制包含两个正整数 ,表示 不能取 。求所有满足条件的 数列的所有元素之积之和。
数据
输入
第一行三个正整数,
接下来
行,每行两个正整数
。
输出
所有满足条件的 数列的所有元素之积之和。
样例
输入
3 4 5
1 1
1 1
2 2
2 3
4 3
输出
90
解释
有一下几种:
数列 | 积 |
---|---|
2 1 1 1 | 2 |
2 1 1 2 | 4 |
2 1 2 1 | 4 |
2 1 2 2 | 8 |
2 1 3 1 | 6 |
2 1 3 2 | 12 |
3 1 1 1 | 3 |
3 1 1 2 | 6 |
3 1 2 1 | 6 |
3 1 2 2 | 12 |
3 1 3 1 | 9 |
3 1 3 2 | 18 |
很明显,表中"积"一项的和为
思路
题目名称叫"容易题",但是我非常想叫它"毒瘤题",我艹太 毒瘤了。。。
虽然样例解释给出的是暴力形式,但是显然我们是不珂以暴力做的。
我们想想(拿
举例),如果没有任何限制,那么积的和是多少呢?
显然,每个位置都珂以取
,合法的数列有
,
,
,
,
这是前四项。如果我们计算它们积的和,我们会发现珂以提一个公因式
出来,然后最后一项是
,即这四个数列积的和为
.当然,我们珂以想象一下,如果我们把所有项都因式分解,应该长这样:
。
此时,我们考虑限制,按样例,第一个不能取
。此时我们发现,分解完的结果长成这样:
就是第一个括号里少加了一个
。也就是说,对于一个限制
,即第
个位置不能取
,对答案的影响就是第
个括号里减去一个
。(从样例中发现,有些限制会重复出现,这很好办,排序一下,然后
循环替代
即可)我们用
表示第
个括号里的值,每次减一下,到最后乘起来就是答案了。
当你开开心心的写完这个代码的时候,你会发现:
!!!
!
所以我们需要优化。发现
并不大,只有
,所以只有这
个做过改动的对答案有影响,其它的就直接快速幂上去即可。由于我们刚刚在去重的时候就已经排过序了,所以代码也就没有什么变动,具体看注释。代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define int long long
#define mod 1000000007
#define K 100100
int n,m,k;
struct node//保存一个约束
{
int pos,val;
//位置,不能等于的值
}a[K];bool operator<(node x,node y){return x.pos<y.pos or (x.pos==y.pos and x.val<y.val);}
//排序:位置第一优先,不能等于的值第二优先
void Input()
{
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=k;++i)
{
scanf("%lld%lld",&a[i].pos,&a[i].val);
}
sort(a+1,a+k+1);//记得排序
}
int cnt[K];//每个括号里的值
//这里只记录被改动过的括号,别的括号里的值用快速幂乘上去
int qpow(int a,int b,int m)//快速幂
{
int r=1;
while(b)
{
if (b&1) r=r*a%m;
a=a*a%m,b>>=1;
}
return r;
}
int Next(int pos)//去重用的,用一个while循环找到第一个不相同的约束条件
//样例里就有一个相同的...真tm毒瘤..
{
++pos;
if (a[pos].pos!=a[pos-1].pos) return pos;
while(a[pos].val==a[pos-1].val) ++pos;
return pos;
}
void Solve()
{
int sum=n*(n+1)/2;sum%=mod;//没有被改动过的括号的值
for(int i=1;i<=k;++i)
{
cnt[i]=sum;
}//初始都是=sum的
int pos=0;//由于有一些不同的约束条件描述的是一个相同的位置(不能等于的值不同),
//所以剩下的括号并不是n-k个,而是用这个pos存一下不同的个数
for(int i=1;i<=k;i=Next(i))
{
if (a[i].pos!=a[i-1].pos)
{
++pos;//如果描述的位置不同,就++
}
cnt[pos]=(cnt[pos]-a[i].val+mod)%mod;//对答案产生影响,所以要修改括号中的值
//不能直接模,要+mod再模
}
int rest=m-pos;//剩下m-pos个括号里都是sum
int ans=qpow(sum,rest,mod);//用快速幂乘上去
for(int i=1;i<=pos;++i)
{
ans*=cnt[i];ans%=mod;
}//这一步上面解释过了
printf("%lld\n",ans);
}
void Main()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
Input();
Solve();
}
#undef int //long long
#undef mod //1000000007
};
main()
{
Flandle_Scarlet::Main();
return 0;
}