省选不杂题乱写(1)
「SDOI2017」相关分析(线段树)
给定序列\(x\),\(y\),有三种操作:
1. 给定\(L,R\)求下式
2.(两个序列分别)区间加(不同值)
3.(两个序列分别)区间修改为(S/T)+i(下标)
sol
分别标记各个部分。
区间加的部分
区间修改的部分
明智的办法,\(\color{orange}{先处理规律的东西,可能使得一些不规律的东西变得规律。}\)我们先将区间修改为下标,再加上\(S\)或\(T\)。
我们得知道
由此来快速算出\(\sum x_i = i^2+(i+1)^2+\cdots+(j-1)^2+j^2=\frac{j(j+1)(2j+1)-i(i-1)(2i-1)}{6}\)
「HAOI2012」高速公路(线段树)
所求即为
分别维护区间\(\sum iv_i\),\(\sum i^2v_i\),\(\sum v_i\)即可。
「SHOI2014」概率充电器(树形DP,up and down,概率)
由于每个节点的价值都是\(1\),实际上就是求通电概率和。
是个树形\(\text{DP}\),运用传说中的\(\color{orange}{\text{up and down}}\)思想。与换根\(\text{DP}\)类似,这也是一种能够处理树上节点信息需要从与其连接各节点(也即包括了子节点与父节点)转移过来这样问题的方法。
明确一个结论:
两个相独立事件\(A,B\),发生的概率分别为\(P(A),P(B)\)
或者可以说是
接下来针对节点\(x\)通电考虑这些相独立的事件:
-
\(x\)自己通电
-
\(x\)的某儿子(通过\(x\)的子树)通电,并且连向该儿子的边通电
-
\(x\)的父亲(通过了\(x\)子树之外的节点)通电,并且与父亲相连的边通电
根据那啥\(\text{up and down}\)思想,可以尝试第一遍先处理\(1\)跟\(2\),得到只考虑这两种情况下的答案,第二遍再处理\(3\)。
首先是\(\text{up}\),根据上面那个独立事件和概率,更新时,对于节点\(x\)以及其子节点\(y\):
然后是\(\text{down}\)。对于一组\(x\)和\(fa_x\),真正的\(P_{fa_x}\)是已经求出的,然后要获得\(fa_x\)在不计算\(x\)时的答案,设这个答案为\(G\)。则:
需要注意的是,这里分母可能是\(0\)。这种情况下由于\(P_x\)已经是\(1\),不需要继续更新。
最后
「SDOI2010」地精部落(DP,排列)
什么妖魔鬼怪题解区,晕了....
求波动排列数。
排列的可用性质?
由于这里只涉及大小比较,我们可以只考虑一个\(\color{orange}{前缀的相对大小的排列}\)。
也就是说\(1\ 2\ 4\)的排列和\(1 \ 2\ 3\)的排列是一样的捏()
并且往后排列只关心相邻两数的大小关系。于是我们可以设状态:
\(f_{i,j,0/1}\)表示\(i\)的排列,最后一个数为\(j\)并且为山谷/峰的波动排列数。则:
复杂度\(O(n^3)\),前缀和和滚动数组优化后\(O(n^2)\)。
浅言一下求排列的题目特征()我自己喜欢把部分离散为相对大小,而常见的做法是加入一个新的最大数,然后考虑这个数和前面每个数交换产生的结果。
[LNOI2014] LCA(LCA,离线)
以前写过为什么现在又忘了啊()
而且也没有做法离线的敏感度了。这里大概是从查询形式是\(l\)到\(r\)来看出,要么是数据结构要么是离线前缀和作差(?
\(\color{orange}{看到查询区间[l,r]还是想想离线处理前缀和做差吧}\)(
具体是这样的,\(\color{orange}{看到深度(dep)要想到这样一个东西:dep_x即为x到根节点的节点个数。}\)\(\color{red}{看到lca我们要想到这样一个做法:标记其中一个点x到根节点的路径上所有点,从另一个点y往上爬,爬到第一个有标记的点就是lca。}\)更进一步地,\(\color{blue}{从这个节点y往上爬,爬到有标记的点的个数就是dep_{lca}。}\)
查询一堆点-一个点的\(lca\)深度和,就可以变成这样一个算法:
将这堆点每个点到根节点的路径每个点权值都\(+1\),再计算这个单点到根节点的权值和。
既然是路径修改就可以树剖辽!
但是我们每次做查询的时候肯定是将某个区间的点按照上述前半句的方式加进来,再对另外某个单点进行这样权值和的查询。\(\color{orange}{既然贡献是累加\color{black}{(操作和的结果等于操作结果的和)},那么我们就可以前缀和作差。}\)
把询问\((-1,l-1,z,qid)\),\((1,r,z,qid)\)离线下来,依次修改每个点到根的路径,到点查询记录答案,做完了()
Luogu P3413 SAC#1 - 萌数(数位DP,单步容斥)(没写)
正着不好做就反着做,我连这个都忘了吗...
这个东西叫单步容斥:\(\color{orange}{合法方案数=总方案数-不合法方案数}\)
使用单步容斥的可循标志是:\(\color{orange}{求至少一个xx的方案数。}\)一般计算不含xx的方案数更好做一点。
既然是至少长度为\(2\)的回文串,我们其实只需要考虑长度为\(2\)或\(3\)的。
要求不带回文串的数,我们只需要不存在\(aa\)以及\(aba\)的情况即可。转移时由于只需要考虑前两个位置,我们设\(f_{i,j,k}\)表示有\(i\)位,最后一位为\(k\),倒数第二位为\(j\)的无回文串数。
感觉能加矩阵..?
哦,等一下,要求的是\(l\)到\(r\)的啊..
[HNOI2015]落忆枫音(DAG,DP,计数)
首先考虑这个\(\text{DAG}\)的答案。给每个节点都选一个父亲,由乘法原理:
然后考虑加一条边成环
加边\(x->y\),那么这条边会与一条\(y->x\)的路径形成一个环。当我们选择了某整个环,就会形成一个不合法方案。
我超,想了一个上午没想明白的原来是这个事情。构造一棵外向树,我只可能选择一条\(y->x\)的路径。
那么每个这样一条包含\(k\)个点的\(y->x\)路径造成的不合法方案数是:
这可不兴算啊(
可以把这种\(\color{orange}{整体的东西分成小步来考虑}\):
当我们在一条\(y->x\)的路径上由\(v->u\)走一步,那么经过了\(v\)的这条路径到达了\(u\)之后,这所有的路径在\(u\)这里就固定了选边,于是方案数会\(\div IN_u\)。设\(f_u\)表示\(y->u\)所有路径固定时的不合法构造方案数,拓扑上\(\text{DP}\),那么有:
其中初始化\(f_y=preANS\)。最后:
记得\(\color{orange}{减模要先加后模哦。}\)
2014 湖北省队互测 week2 已经没有什么好害怕的了(DP,二项式反演)
另外一种能\(\color{orange}{把\color{blue}{恰好}变为\color{red}{至少}以清新化计算的方式:\color{red}{容斥}!}\)(PS:这题实际上并非多步容斥,而是二项式反演。形式上与多步容斥有些类似,但并非“恰好”与“至少”,而是“恰好”与“钦定”的转变。
放个\(yyb老师\)引用的题目:【TopCoder10697】RabbitNumbering
构造\(n\)个整数,每个数可选的区间为\([1,a_i]\),求序列构造方案数。
跟顺序没关系的话很容易想\(\color{orange}{先排个序}\)。这样一来,选择第\(i\)个数的时候,前\(i-1\)个数无论怎么选,都在\([1,a_i]\)这个区间内。于是就有\(ans=\prod\limits_{i=1}^n\color{blue}{(a_i-i+1)}\)。
感觉理解不够,再放一道UVA1485 Permutation Counting
这玩意不大一样,这个\(a,b\)都是排列()
用我们之前遇到排列题目的做法,然后得到以下解法:
设\(f_{i,j}\)表示\(i\)个数,\(E\)值为\(j\)的方案数。
加入第\(i\)个数,如果和前面一个有\(E\)的换,\(\color{red}{或者根本不换}\),那还是\(j\)(
如果和前面一个没E的换,那就\(j+1\)了。得:
好的,回到这个题目,我们同样先排个序
易得要求\(\color{blue}{恰好}\frac{n+k}{2}\)组\(a_x>b_y\)两两匹配方案数。
此题与兔数做法相似的点在于,\(a_1—a_{i-1}\)如果匹配出了\(j\)对\(a_i>b_x\),\(a_i\)要匹配一个小于其的\(b_x\)的方式就会减少\(j\)。
难做的点在于,如果\(a_i\)匹配了一组\(a_i<b_x\),那么我们很难得知\(j>i\),\(a_j\)与\(b_x\)的大小关系。
那么我们就先匹配\(a_i>b_x\)!
其实这个做法叫\(\color{blue}{二项式反演}\)。具体来说:

设\(F_{i,j}\)表示前\(i\)个糖果钦定了\(j\)组\(a_i>b_x\)的方案数,\(P_i\)表示小于\(a_i\)的\(b_x\)个数,那么:
这是先钦定\(j\)个。再有钦定\(j\)个,其余任选的有重方案数\(f_j\):
\(f_j=(n-j)! \times F_{n,j}\)
最后是表示恰好\(i\)组的\(g_i\),满足:
\(f_i=\sum\limits_{j=i}^{n}{j \choose i}g_j\)
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const long long mod=(long long)(1e9+9);
#define MAXN (int)(2e3+7)
int n,k,T;
int a[MAXN],b[MAXN],P[MAXN];
long long F[MAXN][MAXN],C[MAXN][MAXN];
long long fr[MAXN];
long long f[MAXN],g;
inline void INIT() { fr[0]=1; for (int i=1;i<=n;i++) fr[i]=fr[i-1]*i%mod; }
inline void CINIT()
{
C[0][0]=1;
C[1][0]=C[1][1]=1;
for (int i=2;i<=n;i++)
for (int j=0;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][(j-1)>=0?j-1:n+1])%mod;
}
inline int op(int mul) { return (mul&1)?-1:1; }
inline long long maxl(long long A,long long B) { return A>B?A:B; }
int main()
{
scanf("%d%d",&n,&k); INIT(); CINIT();
T=(n+k)/2;
if ((n+k)&1) { puts("0"); return 0; }
for (int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1);
for (int i=1;i<=n;i++) scanf("%d",&b[i]); sort(b+1,b+n+1);
int J=0;
for (int i=1;i<=n;i++)
{
while (J+1<=n&&a[i]>b[J+1]) J++;
P[i]=J;
}
// puts("P:"); for (int i=1;i<=n;i++) printf("%d ",P[i]); puts("");
F[0][0]=1;
for (int i=1;i<=n;i++)
{
// F[i][0]=1;
for (int j=0;j<=i;j++)
{
F[i][j]=((i==j?0:F[i-1][j])+((j>0)?(maxl(P[i]-(j-1),0)*F[i-1][j-1]%mod):0))%mod;
// if (i==1&&j==1) printf("----------- %d %lld %lld %lld\n",P[i]-(j-1),F[i-1][j-1],F[i][j],maxl(P[i]-(j-1),0)*F[i-1][j-1]%mod);
}
}
for (int i=0;i<=n;i++) f[i]=(F[n][i]*fr[n-i])%mod;
// puts("F:");
// for (int i=0;i<=n;i++,cout<<endl)
// for (int j=0;j<=i;j++)
// printf("F[%d][%d]=%lld ",i,j,F[i][j]);
// puts("f:"); for (int i=0;i<=n;i++) printf("%lld ",f[i]); puts("");
for (int j=T;j<=n;j++)
g=(g+op(j-T)*((C[j][T]*f[j])%mod)+mod)%mod;//,printf("::::C[%d][%d]=a%lld\n",j,T,C[j][T]);
printf("%lld\n",g);
return 0;
}
代码千万条,阳间第一条。三目运算不规范,猫猫调试两行泪。
bzoj 2839 集合计数(二项式反演)
同样是二项式反演的题。我先钦定交集中包含了\(i\)个元素,则:
\(g_i\)表示恰好交集大小为\(i\)的集合选择方案数,则:
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAXN (int)(1e6+233)
const long long mod=(long long)(1e9+7);
int fac[MAXN],facv[MAXN];
int n,k;
inline long long qpow(long long A,long long B) { long long base=A,ans=1; while (B) { if (B&1) ans=ans*base%mod; base=base*base%mod; B>>=1; } return ans; }
inline long long sppow(long long A,long long B) { long long base=A,ans=1; while (B) { if (B&1) ans=ans*base%(mod-1); base=base*base%(mod-1); B>>=1; } return ans; }
inline long long optpow(int B) { return (B&1)?-1:1; }
inline void INIT() { fac[0]=1; for (int i=1;i<=n;i++) fac[i]=(int)((fac[i-1]*1ll*i)%mod); facv[n]=(int)(qpow(fac[n],mod-2)%mod); for (int i=n-1;i>=0;i--) facv[i]=(int)((facv[i+1]*1ll*(i+1))%mod); }
inline long long C(int n,int m) { if (n<m) return 0; return ((fac[n]*1ll*facv[m])*1ll%mod*1ll*facv[n-m])%mod; }
int f[MAXN];
long long g=0;
int main()
{
scanf("%d%d",&n,&k); INIT();
for (int i=0;i<=n;i++) f[i]=(int)(C(n,i)*qpow(2,sppow(2,n-i))%mod);//,printf("%lld ",f[i]); puts("end");
for (int i=k;i<=n;i++) g=(g+optpow(i-k)*(1ll*C(i,k)*f[i]%mod)+mod)%mod;
printf("%lld\n",g);
}

浙公网安备 33010602011771号