牛客网暑期ACM多校训练营(第一场)
牛客网暑期ACM多校训练营(第一场)
A. Monotonic Matrix
考虑0和1的分界线,1和2的分界线,发现问题可以转化为两条不互相穿过的路径的方案数(可重叠),题解的做法就是把一条路径斜着平移,然后就转化为不可重叠了。现在考虑,如何计算从(0,0)道(n,m)不相交不可重叠的方案数,一条从(0,1)出发到达(n-1,m),一条从(1,0)出发到达(n,m-1),将他们乘起来的结果还包含相交的情况,于是再减去从(0,1)到(n,m-1)与(1,0)到(n-1,m)的方案数。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
#define MP make_pair
#define fr first
#define sc second
typedef long long ll;
const int N = 1e5 + 7;
const ll mod = 1e9 + 7;
using namespace std;
ll n,m,CC[2005][2005];
ll C(int n,int m) {return CC[n][m];}
int main() {
rep(i,0,2003) CC[i][i]=CC[i][0]=1;
rep(i,2,2003)rep(j,1,i) CC[i][j] = (CC[i-1][j]+CC[i-1][j-1])%mod;
while(scanf("%lld%lld",&n,&m)!=EOF) {
ll ans = ((C(n+m,m)*C(n+m,m))%mod - (C(n+m,m+1)*C(n+m,n+1))%mod+mod)%mod;
printf("%lld\n",ans);
}
return 0;
}
B. Symmetric Matrix
关键在于把这个矩阵,考虑成一个邻接矩阵,然后发现一个每个点有两条边,且无自环,可以有重边。这张图实际上就是,每个点都属于唯一的一个环,环的大小大于等于2。求这种图的方案数。好像第一类斯特灵数?还得递推搞一下。详见:大佬的推导
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define pb push_back
typedef long long ll;
const int N = 1e5 + 7;
using namespace std;
ll dp[N],n,m;
int main() {
dp[1] = 0, dp[2] = 1, dp[3] = 1, dp[4] = 6;
while(scanf("%lld%lld",&n,&m)!=EOF) {
if(n<=4) {
printf("%lld\n",dp[n]%m);
}
else {
ll M;
rep(i,5,n)
M = 1LL*(i-1LL)*(i-2LL)/2LL,M%=m,
dp[i] = (((((i-1)*dp[i-1])%m + ((i-1)*dp[i-2])%m)%m) - (M*dp[i-3])%m + m)%m;
printf("%lld\n",dp[n]);
}
}
return 0;
}
D. Two Graphs
\(n!\) 枚举与 \(G1\) 同构的图,在 \(G2\) 中找到相应的边,如果 \(m1\) 条边都可以匹配到,则将这种方案中用到的边压成一个64位二进制数,放到set里去重。另一种思路是,出现重算的情况就是,这个同构的图与自身的图相同,即使用这些点的映射,形成的新图与原图一致,这种自同构的情况要从答案中除去。
#include <bits/stdc++.h>
typedef unsigned long long ll;
const ll seed = 31;
const ll mod = 1e9 + 7;
inline int read() {
char c = getchar();int x=0, f=1;
while(c<'0'||c>'9'){if(c=='-')f = -1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
using namespace std;
int n,m1,m2,g[11][11],PH[11];
pair<int,int> p[1111];
int main() {
while(scanf("%d%d%d",&n,&m1,&m2)!=EOF) {
set<int> s;s.clear();
memset(g,0,sizeof(g));
for(int i=1;i<=m1;++i) {
int u=read(),v=read();
g[u][v] = g[v][u] = 1;
}
for(int i=1;i<=m2;++i) {
int u=read(),v=read();
p[i] = make_pair(u,v);
}
for(int i=1;i<=n;++i) PH[i]=i;
do{
ll hs=0,cnt=0;
for(ll i=1;i<=m2;++i){
if(g[PH[p[i].first]][PH[p[i].second]]) {
hs |= ((ll)1<<(i-(ll)1));
++cnt;
}
}
if(cnt == m1)s.insert(hs);
}while(next_permutation(PH+1,PH+1+n));
printf("%d\n",(int)s.size());
}
return 0;
}
J. Different Integers
先写了分块莫队tle,然后写了曼哈顿mst莫队tle,然后分块乱狗tle,t到终场。结束后,加了个快读,分块莫队ac..。还有其他一些做法,其实把整个序列在后边复制一份,不就变成了单个区间询问数字种类的模板题了。(让快读成为习惯。。。为何泥萌常数辣么小
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
#define MP make_pair
#define fr first
#define sc second
typedef long long ll;
const int N = 1e5 + 7;
inline int read() {
char c = getchar();int x=0, f=1;
while(c<'0'||c>'9'){if(c=='-')f = -1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
using namespace std;
int n,q,a[N];
struct pp{int l,r,id,ans;}Q[N];
int B,belong[N];
void build() {
B = sqrt(n);
for(int i=1; i<=n; i++) belong[i] = (i-1)/B + 1;
}
bool cmp(pp a, pp b) {
if(belong[a.l] == belong[b.l]) return a.r < b.r;
return belong[a.l] < belong[b.l];
}
int sum=0,num[N];
void update(int p,int d) {
if(num[a[p]]==1&&d==-1) --sum;
else if(num[a[p]]==0&&d==1) ++sum;
num[a[p]]+=d;
}
bool cmp1(pp a,pp b) {
return a.id < b.id;
}
int main() {
while(scanf("%d%d",&n,&q)!=EOF) {
for(int i=1;i<=n;++i)num[i]=0;
build();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<=q;++i) {
Q[i].l=read(),Q[i].r=read();Q[i].id=i;
}
sort(Q+1,Q+1+q,cmp);
int l=0, r=n+1;sum = 0;
for(int i=1;i<=q;++i) {
while(r<Q[i].r)update(r,-1),++r;
while(r>Q[i].r)--r,update(r,1);
while(l<Q[i].l)++l,update(l,1);
while(l>Q[i].l)update(l,-1),--l;
Q[i].ans = sum;
}
sort(Q+1,Q+1+q,cmp1);
for(int i=1;i<=q;++i)printf("%d\n",Q[i].ans);
}
return 0;
}