多校冲刺 NOIP 20211114 模拟 (30)
我是傻B,我是傻B,我是傻B,T2签到题删调试的时候多删了一句有用的,100->30,连样例都没过,我简直就是个脑瘫
还有5天整就要NOIP了,或许这是我学OI的最后5天了,以后再摸到算法竞赛或许就要到大学打ACM了
希望NOIP的时候无怨无悔吧
T1 构造字符串
脑瘫题,直接并查集维护相同的字符,\(bool\)数组维护不能相同的点,然后瞎乱写就行了
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=1005;
inline void chmin(int &x,int &y){if(x>y)swap(x,y);}
struct node{int x,y,z;}p[maxn];
int n,m,bo[maxn][maxn],a[maxn],fa[maxn];
bool cmp(node a,node b){return a.z>b.z;}
bool vis[maxn];
inline int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
inline void merge(int x,int y)
{
int fx=getfa(x),fy=getfa(y);
if(fx==fy) return ;
if(fx<fy) fa[fy]=fx;
else fa[fx]=fy;
}
signed main()
{
freopen("str.in","r",stdin);
freopen("str.out","w",stdout);
n=read();m=read();
for(int i=1;i<=m;i++)
{
p[i].x=read(); p[i].y=read(); p[i].z=read();
if(p[i].x==p[i].y&&p[i].z==-1){puts("-1");return 0;}
chmin(p[i].x,p[i].y);
}
for(int i=1;i<=n;i++) fa[i]=i;
sort(p+1,p+1+m,cmp);
for(int i=1;i<=m;i++)
{
if(p[i].z==0)
{
if(getfa(p[i].x)==getfa(p[i].y)) {puts("-1");return 0;}
else {bo[p[i].x][p[i].y]=-1;}
}
else
{
for(int j=p[i].x,k=p[i].y;j<=p[i].x+p[i].z-1;j++,k++) {merge(j,k);}
if(p[i].y+p[i].z<=n) {bo[p[i].x+p[i].z][p[i].y+p[i].z]=-1;}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++) if(bo[j][i]==-1)
if(getfa(i)==getfa(j)){puts("-1");return 0;}
else bo[min(getfa(i),getfa(j))][max(getfa(i),getfa(j))]=-1;
}
for(int i=1;i<=n;i++)
{
if(getfa(i)==i)
{
memset(vis,0,sizeof(vis));
for(int j=1;j<i;j++) if(bo[min(getfa(i),getfa(j))][max(getfa(i),getfa(j))]==-1)
vis[a[j]]=1; for(int j=0;j<i;j++) if(!vis[j]){a[i]=j;break;}
}
else a[i]=a[getfa(i)];
}
for(int i=1;i<=n;i++) printf("%d ",a[i]);
}
T2 寻宝
也是个脑瘫题,显然可以先用并查集将所有点先缩一下,发现k<=100,所以\(k^2\)跑下\(dfs\)然后\(O(1)\)回答询问就行了
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=50005;
int fa[maxn],n,m,k,q,jb;
inline int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
inline void merge(int x,int y)
{int fx=getfa(x),fy=getfa(y); if(fx==fy)return; fa[fx]=fy;}
vector<bool> vec[maxn];
inline int id(int x,int y){return (x-1)*m+y;}
char s[maxn];
int pos[maxn],vis[maxn],cnt,bel[maxn];
int head[maxn],dfsx,scc_num,siz[maxn],num;
int stk[maxn],top,du[maxn],bh[maxn];
struct edge{int to,nxt;}e[maxn<<1];
inline void add(int x,int y){e[++num]=(edge){y,head[x]};head[x]=num;du[x]++;du[y]++;}
namespace solve1{
bool bo[1003][1003],vs[1003];
inline void dfs(int x)
{
vs[bh[x]]=1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(!vs[bh[y]]) dfs(y);
}
}
inline void solve1()
{
for(int i=1;i<=cnt;i++)
if(du[i]) stk[++top]=i,bh[i]=top;
for(int i=1;i<=top;i++)
{
memset(vs,0,sizeof(vs));
dfs(stk[i]);
for(int j=1;j<=top;j++)
bo[bh[stk[i]]][j]=vs[j];
}
for(int i=1;i<=q;i++)
{
int p1=read(),p2=read(),p3=read(),p4=read();
int x=id(p1,p2),y=id(p3,p4);
if(getfa(x)==getfa(y)){puts("1");continue;}
if(bo[bh[bel[getfa(x)]]][bh[bel[getfa(y)]]]){puts("1");continue;}
puts("0");
}
}
}
signed main()
{
freopen("treasure.in","r",stdin);
freopen("treasure.out","w",stdout);
n=read();m=read();k=read();q=read();
for(int i=1;i<=n*m;i++) fa[i]=i;
for(int i=0;i<=m+2;i++) vec[0].push_back(1),vec[n+1].push_back(1);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
vec[i].push_back(1);
for(int j=1;j<=m;j++)
if(s[j]=='#') vec[i].push_back(1);
else vec[i].push_back(0);
vec[i].push_back(1);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
if(vec[i][j]==0)
{
if(j!=m) if(vec[i][j+1]==0) merge(id(i,j),id(i,j+1)),assert(getfa(id(i,j))==getfa(id(i,j+1)));
if(j!=1) if(vec[i][j-1]==0) merge(id(i,j),id(i,j-1)),assert(getfa(id(i,j))==getfa(id(i,j-1)));
if(i!=n) if(vec[i+1][j]==0) merge(id(i,j),id(i+1,j)),assert(getfa(id(i,j))==getfa(id(i+1,j)));
if(i!=1) if(vec[i-1][j]==0) merge(id(i,j),id(i-1,j)),assert(getfa(id(i-1,j))==getfa(id(i,j)));
}
}
for(int i=1;i<=n*m;i++) if(getfa(i)==i) pos[++cnt]=i,bel[i]=cnt;
for(int i=1;i<=k;i++)
{
int p1=read(),p2=read(),p3=read(),p4=read();
int x=bel[getfa(id(p1,p2))],y=bel[getfa(id(p3,p4))];
if(x!=y) add(x,y);
}
solve1::solve1();
}
T3 序列
考到知识盲区了,上次打李超树是什么时候来着??全都忘了,以后还是要把所有学过的板子全部熟练掌握才好
不过或许没有以后了
首先比较容易发现这是一个和斜率有关的题目,那么我们将题目转化一下
首先答案显然可以转化成跨过\(p_i\)的区间容易转化为:以\(p_i-1\)为右端点的最优+以\(p_{i}\)为左端点的最优
那么再转化一下就是右端点在\(p_{i}\)右边,左端点为1的最大区间,减去右端点在\(p_{i}\)左边,右端点为1的最小值
那么可以先把答案离线下来,由于k只有2e6种取值,直接李超树维护一下最值就行了
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=2e6+5,maxm=1e6+1;
int a[maxn],b[maxn],n,m;
struct query{int p,k,id;}p[maxn];
int ans[maxn];
bool cmp(query a,query b){return a.p<b.p;}
struct node{int k,b;};
struct tree{
#define lid id<<1
#define rid id<<1|1
node tr[maxn<<2];
inline void init(){for(int i=1;i<=(maxn<<1);i++)tr[i].b=-10000000000000000ll;}
inline int calc(node num,int x){return num.k*x+num.b;}
inline bool cover(node a,node b,int x){return calc(a,x)<=calc(b,x);}
inline void insert(int id,int l,int r,node num)
{
if(cover(tr[id],num,l-maxm)&&cover(tr[id],num,r-maxm)){return tr[id]=num,void();}
if(l==r) return ;int mid=(l+r)>>1;
if(cover(tr[id],num,mid-maxm)) swap(tr[id],num);
if(cover(tr[id],num,l-maxm)) insert(lid,l,mid,num);
if(cover(tr[id],num,r-maxm)) insert(rid,mid+1,r,num);
}
inline int query(int id,int l,int r,int x)
{
int mid=(l+r)>>1,ans=-10000000000000000ll;
if(x<mid) ans=max(ans,query(lid,l,mid,x));
if(x>mid) ans=max(ans,query(rid,mid+1,r,x));
ans=max(ans,calc(tr[id],x-maxm));
return ans;
}
}T[2];
signed main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
n=read(); m=read();
for(int i=1;i<=n;i++) a[i]=read()+a[i-1],b[i]=read()+b[i-1];
for(int i=1;i<=m;i++) p[i].p=read(),p[i].k=read(),p[i].id=i;
sort(p+1,p+1+m,cmp); int tmp=1; T[0].init();
for(int i=1;i<=m;i++)
{
while(tmp<p[i].p)
{T[1].insert(1,1,maxm<<1,(node){b[tmp],-a[tmp]});tmp++;}
int tmp=T[1].query(1,1,maxm<<1,p[i].k+maxm);
ans[p[i].id]+=tmp;
}
tmp=n;
for(int i=m;i>=1;i--)
{
while(tmp>=p[i].p)
{T[0].insert(1,1,maxm<<1,(node){-b[tmp],a[tmp]});tmp--;}
ans[p[i].id]+=T[0].query(1,1,maxm<<1,p[i].k+maxm);
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}
T4 构树
超级神仙题目,虽然有2,30分是白送的,不过没有兴趣拿
首先可以直接枚举所有的树,一共有\(n^{n-2}\)种,这个的话看枚举方法了,可以拿20~30分不等
然后还有一个状压做法:
假装我们的树是一颗以1为根的有根树,模拟展开dfs子树关系的过程
另\(dp_{i,S,j}\)表示以\(i\)为根,子树里已经已经有\(S\)集合的节点,有\(j\)条边相同
然后为一个\(dp_{i,S,j}\)枚举一个儿子\(v\)以及其子树\(T\)进行合并
复杂度是\(O(3^nn^w)\)
神仙赵\(sir\)提供了一种复杂度为\(O(\sum\limits_{i=1}^{i<=n}\binom{n}{i}*(n-i)^3)\)的吊做法
不过需要矩阵树定理和行列式,我不会所以咕了。。。
下面进入正题
首先\(Cayley\)公式指出一棵无根树最多有\(n^{n-2}\)种形态,证明的话就是\(prufer\)序列最多有\(n^{n-2}\)种
扩展\(Cayley\)公式:被确定边分为大小为\(a_1,a_2,\cdots, a_m\)的连通块,则有\(n^{m-2}\prod {a_i}\)种生成树
知道这两个公式之后就可以拓展不少的思路了
首先我们设\(dp_{i,j,k}\)表示在\(i\)的联通块大小为\(j\),至少有\(k\)条边强制不选的方案数
那么转移的话就是枚举一个儿子\(v\)
这是强制不选那条连接\(i,v\)的边
\(z*dp_{i,j,k}*dp_{v,z,l}-dp_{i,j,k+l}\)
这是强制选的
\(dp_{i,j,k}*dp_{v,z,l}-dp_{i,j+z,k+l-1}\)
最后统一乘上那一堆\(n\),然后再用二项式反演容斥一下就出了
不过无法通过,考虑对这个过程进行优化
优化的话就是一个听说比较经典的一个优化,就是在中间转移的时候乘的那个系数可以转化为在\(siz\)中选择一个点,
那么可以用\(0,1\)表示有选择那个点和没有选择那个点进行转移,然后就\(O(n^2)\)了
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=8005,mod=1e9+7;
int jc[maxn],inv[maxn],n,siz[maxn],tmp[2][maxn],ans[maxn];
vector<int>vec[maxn],dp[maxn][2];
inline int C(int x,int y)
{
return jc[x]*inv[y]%mod*inv[x-y]%mod;
}
inline int ksm(int x,int y)
{
int res=1;x=x%mod;
for(;y;y>>=1){if(y&1)res=res*x%mod;x=x*x%mod;}
return res;
}
inline void init()
{
jc[0]=inv[0]=1;
for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod,inv[i]=ksm(jc[i],mod-2);
}
inline void dfs(int x,int f)
{
siz[x]=1;dp[x][0].resize(2);dp[x][1].resize(2);
dp[x][0][1]=dp[x][1][1]=1;
for(auto v:vec[x])if(v!=f)
{
dfs(v,x);
for(int i=0;i<=siz[x]+siz[v];i++) tmp[0][i]=tmp[1][i]=0;
for(int i=1;i<=siz[x];i++) for(int j=1;j<=siz[v];j++)
{
tmp[0][i+j]=(tmp[0][i+j]+dp[x][0][i]*dp[v][1][j])%mod;
tmp[1][i+j]=(tmp[1][i+j]+dp[x][1][i]*dp[v][1][j])%mod;
tmp[0][i+j-1]=(tmp[0][i+j-1]+dp[x][0][i]*dp[v][0][j])%mod;
tmp[1][i+j-1]=(tmp[1][i+j-1]+dp[x][0][i]*dp[v][1][j]+dp[x][1][i]*dp[v][0][j])%mod;
}
siz[x]+=siz[v];dp[x][0].resize(siz[x]+1);dp[x][1].resize(siz[x]+1);
dp[v][0].clear();dp[v][1].clear();
for(int i=0;i<=siz[x];i++) dp[x][0][i]=tmp[0][i],dp[x][1][i]=tmp[1][i];
}
}
signed main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();init();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
vec[x].push_back(y);
vec[y].push_back(x);
}
dfs(1,0);int base=1;ans[n-1]=1;
for(int i=n-2;~i;i--,base=base*n%mod) ans[i]=dp[1][1][n-i]*base%mod;
for(int i=0;i<n;i++)
{
int res=0;
for(int j=i,base=1;j<n;j++,base*=-1) res=(res+base*ans[j]*C(j,i)%mod);
printf("%lld ",(res%mod+mod)%mod);
}
}