如果说前三场都是因为各种智障错误爆炸,这场就终于是凭实力爆炸,打了三个暴力,美滋滋。
rank1 3hAK。我还有什么话可说呢。只有ORZ%%%。
其实T3我知道这种最大流求法因为之前做过谁谁养兔子那题,然后我用这玩意打了第二档暴力。。思考了1s是继续想T3还是去做还没有动的T1我非常不明智地选择了后者于是最后1.5h我成功地得到了10pt并且再也没有任何动静。如果继续延续那1s的T3可能用这个东西dp就可以搞搞的思路感觉出正解并不是很困难,,,都知道结论了。。。我大概是睿智本智吧。。。
原题在这里,被出题人搬来了
被rank1,2,3切了,其余没有超过10分的。
因为菜宸完全不懂容斥只会枚举有几个数相等然后用组合数做容斥,然后只能过k<=3的点。。显然他们之间的相等关系没有这么简单,某一坨相等为一个数另一坨相等为另个数之类的。
那如何容斥呢。把相等看成边,k个数看成点,某两个数相等就在两个点之间连边,这样枚举完全图的所有子图就相当于完美地枚举所有相等关系了。
于是开始枚举子图了。根据乘法原理,下面三部分的答案是相乘的。
枚举子图当然是首先枚举每个连通块大小,设分成i个连通块,第x个连通块大小为sz[x],大小为x的连通块有cnt[x]个,把k个点分到这些连通块中的方案就是
! k ∏ ! s z x ∏ ! c n t x \frac{!k}{\prod !sz_x\prod !cnt_x} ∏!szx∏!cntx!k
宸式感性理解:联通块们排队依次选自己需要的所有点( ! k !k !k),每个连通块在选自己的sz个点时顺序是无所谓的( ∏ ! s z x \prod !sz_x ∏!szx)。一些大小相同的小联通块,他们排队选点的先后顺序也是无所谓的( ∏ ! c n t x \prod !cnt_x ∏!cntx)。
确定了大小后联通块的内部究竟长相如何?我们都知道如何算大小为n的连通块数目,设g[n]为大小为n的联通块数目,f[n]为大小为n的图的数目.枚举1所在联通块
f n = 2 n ∗ ( n − 1 ) 2 g n = f n − ∑ i = 1 n − 1 ( n − 1 i − 1 ) g i ∗ f n − i f_n=2^{\frac{n*(n-1)}{2}} \\g_n=f_n-\sum_{i=1}^{n-1} \binom{n-1} {i-1}g_i*f_{n-i} fn=22n∗(n−1)gn=fn−i=1∑n−1(i−1n−1)gi∗fn−i
现在我们构造若干连通块作为一个图,一个图的容斥系数是 ( − 1 ) e c n t (-1)^{ecnt} (−1)ecnt, e c n t ecnt ecnt为边数。这玩意显然是可以乘法原理计算每个连通块的容斥系数再相乘的,我们就可以直接算每个连通块的所有连通方式的容斥系数和,然后再相乘。
然后,我们发现把f和g定义为容斥系数和,依然满足 g n = f n − ∑ i = 1 n − 1 ( n − 1 i − 1 ) g i ∗ f n − i g_n=f_n-\sum_{i=1}^{n-1} \binom{n-1} {i-1}g_i*f_{n-i} gn=fn−∑i=1n−1(i−1n−1)gi∗fn−i
算f枚举完全图的所有子图。枚举子图边数
f n = ∑ i = 0 n ∗ ( n − 1 ) / 2 ( − 1 ) i ( n ∗ ( n − 1 ) / 2 i ) = [ n = 1 ] f_n=\sum_{i=0}^{n*(n-1)/2}(-1)^i\binom{n*(n-1)/2}{i}=[n=1] fn=i=0∑n∗(n−1)/2(−1)i(in∗(n−1)/2)=[n=1]
带入得到
g n = − ( n − 1 ) ∗ g n − 1 g_n=-(n-1)*g_{n-1} gn=−(n−1)∗gn−1
哎呀我的妈呀终于把图构造好了万事俱备只欠数进去了。质因子显然也是互不相关可以方案相乘的,且放质因子进图的方案和它本人是啥子没有关而只和它的数目有关。
第一部分的答案我们对每种连通块大小进行搜索,第二部分在搜索的过程中乘上每一块的g,现在这部分就在搜索的过程中搞一个dp, d p [ i ] [ j ] dp[i][j] dp[i][j]表示把j个质因子放进目前搜到的i个块中的方案数,转移会发现可以做成一个以每个块的大小为物品体积的完全背包(意会吧不扯了)。
一些优化效率的小trick。
搜索的时候肯定是按块的大小从小往大搜,剩下的数的个数是last,上一个块大小是psz时,下一个块的大小只需要搜psz~last/2和last。
完全背包是一维来着,开两维是为了回溯上一层的背包状态,一边搜一边背,搜完再背可能会TLE。
//Achen
#include
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=55555,mod=1e9+7;
typedef long long LL;
typedef double db;
using namespace std;
int n,k;
int p[N],c[N],mx;
LL ans,fac[N],inv[N],g[N];
template<typename T>void read(T &x) {
char ch=getchar() ; T f=1; x=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
LL mo(LL x) { return x<0?x+mod:(x>=mod?x-mod:x); }
LL C(int n,int m) { return fac[n]*inv[m]%mod*inv[n-m]%mod; }
LL fen(int n,int m) { return C(n+m-1,m-1); }
void pre() {
fac[0]=fac[1]=1;
inv[0]=inv[1]=1;
For(i,2,k) fac[i]=fac[i-1]*i%mod;
For(i,2,k) inv[i]=mo(mod-(LL)mod/i*inv[mod%i]%mod);
For(i,2,k) inv[i]=inv[i-1]*inv[i]%mod;
For(i,2,n) {
int fl=1,up=sqrt(i);
For(j,2,up) if(i%j==0) {
fl=0; break;
}
if(fl) p[++p[0]]=i;
}
For(i,2,n) {
int x=i;
For(j,1,p[0]) {
while(x%p[j]==0) {
c[j]++;
x/=p[j];
}
}
}
mx=c[1];
g[1]=1;
For(i,2,k) g[i]=mo(mod-g[i-1]*(i-1)%mod);
}
LL f[32][N];
void dfs(int pos,int psz,int pcnt,int last,LL now) {
if(!last) {
For(i,1,p[0]) now=now*f[pos-1][c[i]]%mod;
ans=mo(ans+now);
return ;
}
if(last<psz) return ;
if(psz) {
For(s,0,mx) f[pos][s]=mo(f[pos-1][s]+(s>=psz?f[pos][s-psz]:0));
dfs(pos+1,psz,pcnt+1,last-psz,now*g[psz]%mod*inv[psz]%mod*fac[pcnt]%mod*inv[pcnt+1]%mod);
}
For(x,psz+1,last/2) {
For(s,0,mx) f[pos][s]=mo(f[pos-1][s]+(s>=x?f[pos][s-x]:0));
dfs(pos+1,x,1,last-x,now*g[x]%mod*inv[x]%mod);
}
if(psz!=last) { int x=last;
For(s,0,mx) f[pos][s]=mo(f[pos-1][s]+(s>=x?f[pos][s-x]:0));
dfs(pos+1,x,1,last-x,now*g[x]%mod*inv[x]%mod);
}
}
int main() {
freopen("noip.in","r",stdin);
freopen("noip.out","w",stdout);
read(n); read(k);
pre(); f[0][0]=1;
dfs(1,0,0,k,1);
printf("%lld\n",ans);
Formylove;
}
出题人:这是一道noip简单题。
对于 c b a ∣ a b c d e f ∣ f e cba|abcdef|fe cba∣abcdef∣fe这样没有出现过完整ab的串一遍马拉车扫过去就可以得到答案。
考虑出现过完整循环的形如 b a ∣ a b c d d c b a ∣ a b c d d c b a ∣ a b c d d c b a ∣ a b c d d ba|abcddcba|abcddcba|abcddcba|abcdd ba∣abcddcba∣abcddcba∣abcddcba∣abcdd的串
我们知道循环部分是可以kmp找循环节的,但是前后有一部分不完整的部分,这非常不好考虑,于是智障宸就只会枚举左边多出的部分剩下的部分跑kmp,这样怎么复杂度都炸了,不如直接打暴力。
然鹅,显然这样的串都可以通过前移这个|改变划分变成只有后面有多出部分的形式,我们先只考虑他这样的形态就好了。
b a ∣ a b c d d c b a ∣ a b c d d c b a ∣ a b c d d c b a ∣ a b c d d ba|abcddcba|abcddcba|abcddcba|abcdd ba∣abcddcba∣abcddcba∣abcddcba∣abcdd
↓
b a a b c d d c ∣ b a a b c d d c ∣ b a a b c d d c ∣ b a a b c d d baabcddc|baabcddc|baabcddc|baabcdd baabcddc∣baabcddc∣baabcddc∣baabcdd
然后就可以跑一遍kmp判断这个串是否有一个长度为l的循环节了。且如果有一个长度为l的循环节,任意一个长度为l的子串都是这个串的某种循环划分的一个循环节。
我们需要找循环节是回文串的那些划分,只要找到向左右扩展大于等于l/2的回文中心,就能找到所有划分点和两个划分点中间的回文中心点了(因为上一段那句话)。
会找到形如这样的(每种合法的情况的点都找到了,下面是某种合法的情况)
b a a b c d ∣ d c b a ∣ a b c d ∣ d c b a ∣ a b c d ∣ d c b a ∣ a b c d d baabcd|dcba|abcd|dcba|abcd|dcba|abcdd baabcd∣dcba∣abcd∣dcba∣abcd∣dcba∣abcdd
每个完整的块就是题目中要求的一个合法答案,然鹅这样的话找到的划分点是比完整块数少1的(最前和最后的完整块的左右没有被包起来呢)!也就是每次答案应该再加上划分的方式的种数。
那很好办啊,找到 [ l , 2 ∗ l ) [l,2*l) [l,2∗l)内的划分点的数目就好了,这些划分点一定是一种划分的开始点啊。
现在只需要支持找大于等于l的回文中心数目,和他们中在某个区间内的数目,按l排序后树状数组搞一搞即可。
//Achen
#include
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=2000007;
typedef long long LL;
typedef double db;
using namespace std;
int n;
char s[N],ss[N];
LL ans;
template<typename T> void read(T &x) {
char ch=getchar(); T f=1; x=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
int rad[N],r[N];
void manacher() {
int tot=0;
ss[++tot]='&';
ss[++tot]='#';
For(i,0,n-1) {
ss[++tot]=s[i];
ss[++tot]='#';
}
ss[++tot]='*';
for(int i=1,k=0,j;i<=tot;) {
while(ss[i-k-1]==ss[i+k+1]) k++;
rad[i]=k;
for(j=1;j<=k&&rad[i-j]!=rad[i]-j;j++)
rad[i+j]=min(rad[i-j],rad[i]-j);
i+=j;
k=max(k-j,0);
}
int cc=1,nl=0;
For(i,1,n) {
r[i]=rad[(i+1)*2];
while(nl+1<i&&(i-nl-1)>n-i&&(i-nl-1)>nl+1) {
nl++;
if(r[nl]/2>=nl) cc++;
}
if(r[i]/2>=n-i&&n-i<i) ans+=cc;
} ans--;
}
int sum[N];
int add(int x,int v) {
for(int i=x;i<=n;i+=(i&(-i)))
sum[i]+=v;
}
int qry(int x) {
int rs=0;
for(int i=x;i;i-=((i&(-i))))
rs+=sum[i];
return rs;
}
#define MP make_pair
#define pr pair
#define fi first
#define se second
set<pr>S;
int nxt[N],sl[N];
void kmp() {
for(int i=1,k=0;i<n;i++) {
while(k&&s[i]!=s[k]) k=nxt[k-1];
if(s[i]==s[k]) k++;
nxt[i]=k;
}
int k=nxt[n-1],fl=0;
for(;;) {
int l=n-k;
if(!(l&1)) sl[++sl[0]]=l;
if(!k) break;
k=nxt[k-1];
}
For(cs,1,sl[0]) {
int l=sl[cs];
if(!fl) {
For(i,1,n-1) if(r[i]>=l) {
add(i,1);
S.insert(MP(r[i],i));
}
fl=1;
}
else {
while(!S.empty()) {
pr t=*S.begin();
if(t.fi<l) {
add(t.se,-1);
S.erase(S.begin());
}
else break;
}
}
ans+=qry(n)+qry(l-1)-qry(l/2-1);
}
}
int main() {
freopen("oip.in","r",stdin);
freopen("oip.out","w",stdout);
scanf("%s",s);
n=strlen(s);
manacher();
kmp();
printf("%lld\n",ans);
Formylove;
}
原题在这里,被出题人搬来了
给出的列的数组叫b,行的数组就叫a吧。
第二个部分分显然在暗示你,容易想到枚举a的每一位然后网络流判断是否合法。判最大流是否等于b的和即可。然后最大流等于最小割,把a,b排序,最小割就是
c u t = M i n A , B ( ∑ i = 1 A a [ i ] + ∑ i = 1 B b [ i ] + ( n − A ) ∗ ( m − B ) ) cut=Min_{A,B}(\sum_{i=1}^Aa[i]+\sum_{i=1}^Bb[i]+(n-A)*(m-B)) cut=MinA,B(i=1∑Aa[i]+i=1∑Bb[i]+(n−A)∗(m−B))
满足条件的a就是 c u t ≥ s u m b [ m ] cut \geq sumb[m] cut≥sumb[m],即任意 A , B A,B A,B,满足
∑ i = 1 A a [ i ] + ∑ i = 1 B b [ i ] + ( n − A ) ∗ ( m − B ) ≥ s u m b [ m ] \sum_{i=1}^Aa[i]+\sum_{i=1}^Bb[i]+(n-A)*(m-B)\geq sumb[m] ∑i=1Aa[i]+∑i=1Bb[i]+(n−A)∗(m−B)≥sumb[m]
枚举A,B,对于排序后a的每个前缀和,可以得到一个限制, s u m a [ i ] ≥ x [ i ] suma[i] \geq x[i] suma[i]≥x[i];
既然如此只要dp求出满足这个条件的所有a数组就好啦。
设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示a中前i小的数已经放进a数组的某些位置了,其中最大的数为j,前i小的数的和为k的方案数,转移需要枚举新加的数放多少个,从f[i-k]用组合数转移到f[i]。j这一维用前缀和优化就好。 o ( n 5 ) o(n^5) o(n5)。
//Achen
#include
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=55,p=1000000007;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,b[N],a[N];
LL ans,C[N][N],f[N][N][N*N];
template<typename T>void read(T &x) {
char ch=getchar() ; T f=1; x=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
void pre() {
For(i,0,N-1) C[i][0]=1;
For(i,1,N-1) For(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
sort(b+1,b+m+1);
For(i,1,m) b[i]+=b[i-1];
For(B,0,m) For(A,0,n)
a[A]=max(a[A],b[m]-b[B]-(m-B)*(n-A)); //b[B]+a[A]+(m-B)*(n-A)>=b[m]
}
LL mo(LL x) { return x>=p?x-p:x; }
int main() {
freopen("ip.in","r",stdin);
freopen("ip.out","w",stdout);
read(n); read(m);
For(i,1,m) read(b[i]);
pre();
For(i,0,n) {
if(a[i]>0) break;
f[i][0][0]=C[n][i];
}
For(i,0,n) {
if(i) For(x,1,m) For(s,a[i],b[m]) For(j,1,i) {
if(s-j*x+x<a[i-j+1]) break;
if(s-j*x>=0) f[i][x][s]=mo(f[i][x][s]+f[i-j][x-1][s-j*x]*C[n-i+j][j]%p);
}
For(s,a[i],b[m]) For(x,1,m)
f[i][x][s]=mo(f[i][x][s]+f[i][x-1][s]);
}
printf("%lld\n",f[n][m][b[m]]);
Formylove;
}