模拟[懒得编号]考试总结

我要是不退役我一定把编号修上

考试经过

T1想了想觉得回了就写,T2确实很水就秒了,然后去看T3发现不会
我TM又不会李超树写个鬼啊 我怎么可能考场写线段树凸包啊于是写了暴力
然后全力肝T4,目标60分状压,然后开写,然后发现算重,然后改,然后继续算重……
重复n遍以后发现已经11:30了,于是开冲dfs,在52过了样例,发现只能跑\(n=9\),管那么多干啥啊,交了
交上去一看人傻了,T1程序里的assert(0)没删,当场暴毙
最后看分,在T1没炸的情况下,50+100+50+25=225,发现没炸也啥也不是,退役分滚粗

T1.构造字符串

写挂的原因是只用并查集判断无解了,没用来构造答案就假了
发现可以转化成若干个\(a_i=b_i\)\(a_i!=b_i\)的限制,所以对同类的缩到一起
如果一样的之间存在不等关系则无解,否则一次确定一类的取值,枚举全部取\(mex\)即可

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1005;
int p[N][N],n,m,f[N];
inline void die(){puts("-1");exit(0);}
inline int find(int x)
{	
	if(f[x]!=x)f[x]=find(f[x]);
	return f[x];
}
int ans[N];bool v[N],t[N][N];
vector <int> pp[N];
signed main()
{
	freopen("str.in","r",stdin);
	freopen("str.out","w",stdout);
	cin>>n>>m;
	memset(p,-1,sizeof(p));
	for(int i=1;i<=n;i++)p[i][i]=1;
	for(int i=1;i<=m;i++)
	{
		int x,y,z;scanf("%lld%lld%lld",&x,&y,&z);		
		for(int j=1;j<=z;j++)
		{		
			if(!p[x][y])die();p[x][y]=p[y][x]=1;
			x++;y++;
		}
		if(p[x][y]==1)die();p[x][y]=p[y][x]=0;
	}
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		 if(p[i][j]==1)f[find(j)]=find(i);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		if(p[i][j]==0)
		{
			int px=find(i),py=find(j);
			if(px==py)die();
			t[px][py]=t[py][px]=1;	
		}
	}
	for(int i=1;i<=n;i++)pp[find(i)].push_back(i);
	memset(ans,-1,sizeof(ans));
	for(int i=1;i<=n;i++)
	{
		if(ans[i]==-1)
		{		
			int ff=find(i);memset(v,0,sizeof(v));int an;
			for(int j=1;j<=n;j++)if(t[find(j)][ff]&&ans[find(j)]!=-1)v[ans[find(j)]]=1;
			for(int j=0;j<=n;j++)if(!v[j]){an=j;break;}
			for(int j=0;j<pp[ff].size();j++)ans[pp[ff][j]]=an;
		}
	}
	for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
	return 0;
}

T2.寻宝

先用bfs把联通块缩起来,然后对100个门跑传递闭包,没门的不用管
一个询问如果在一块里直接可达,不在一块里如果可达就有解

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define pb push_back
#define pir pair<int,int>
#define mp make_pair
const int N=50005;
inline int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
vector<int> a[N],p[N];
char s[N];int n,m,k,qq,tot,cnt;
queue <pir> q;map <int,int> mmp;
inline void bfs(int px,int py,int id)
{
	q.push(mp(px,py));p[px][py]=id;
	while(q.size())
	{	
		int x=q.front().first,y=q.front().second;q.pop();
		if(x>1&&a[x-1][y]==1&&p[x-1][y]==0)p[x-1][y]=id,q.push(mp(x-1,y));
		if(y>1&&a[x][y-1]==1&&p[x][y-1]==0)p[x][y-1]=id,q.push(mp(x,y-1));
		if(x<n&&a[x+1][y]==1&&p[x+1][y]==0)p[x+1][y]=id,q.push(mp(x+1,y));
		if(y<m&&a[x][y+1]==1&&p[x][y+1]==0)p[x][y+1]=id,q.push(mp(x,y+1));
	}
}
bool b[205][205];
signed main()
{
	freopen("treasure.in","r",stdin);
	freopen("treasure.out","w",stdout);
	cin>>n>>m>>k>>qq;
	for(int i=0;i<=m+1;i++)a[0].pb(0),p[0].pb(0);
	for(int i=1;i<=n;i++)
	{
		a[i].pb(0);p[i].pb(0);scanf("%s",s+1);
		for(int j=1;j<=m;j++)
		{
			if(s[j]=='#')a[i].pb(0);
			else a[i].pb(1);
			p[i].pb(0);
		}
		a[i].pb(0);p[i].pb(0);
	}
	for(int i=0;i<=m+1;i++)a[n+1].pb(0),p[n+1].pb(0);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
		if(p[i][j]==0&&a[i][j]==1)bfs(i,j,++tot);
	for(int i=1;i<=k;i++)
	{
		int x=read(),y=read(),xx=read(),yy=read();
		int id1=p[x][y],id2=p[xx][yy],p1,p2;
		if(mmp.find(id1)==mmp.end())mmp[id1]=++cnt,p1=cnt,b[p1][p1]=1;
		else p1=mmp[id1];
		if(mmp.find(id2)==mmp.end())mmp[id2]=++cnt,p2=cnt,b[p2][p2]=1;
		else p2=mmp[id2];
		b[p1][p2]=1;
	}
	for(int kk=1;kk<=cnt;kk++)for(int i=1;i<=cnt;i++)for(int j=1;j<=cnt;j++)
	 b[i][j]=b[i][j]|(b[i][kk]&b[kk][j]);
	for(int i=1;i<=qq;i++)
	{
		int x=read(),y=read(),xx=read(),yy=read();
		int id1=p[x][y],id2=p[xx][yy];
		assert(id1&&id2);
		if(id1==id2){puts("1");continue;}
		if(mmp.find(id1)==mmp.end()||mmp.find(id2)==mmp.end()){puts("0");continue;}
		int p1=mmp[id1],p2=mmp[id2];
		if(b[p1][p2])puts("1");else puts("0");
	}
	return 0;
}

T3.序列

被称李超树模板题,于是滚去学了李超树
左右贡献相互独立,可以把柿子拆开,两个问题同理,以右端点为例
\(sa_i=\sum_{j=1}^i a_j\)\(sb_i=\sum_{j=1}^ib_j\)
最优即\(\max_{1\leq l\leq r}\{(sa_{r}-sa_{l-1})-k(sb_{r}-sb_{l-1})\}\)
\(sa_r-k\cdot sb_{r}+\max_{0\leq l<r}\{ksb_{l}-sa_l\}\),离线之后李超树维护直线即可

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1000050;
int n,m,ans[N],pa[N],pb[N],suma[N],sumb[N];
struct line{int k,b;}a[N];
struct node{int id,k;};vector <node> p[N];
struct tree{
	int l,r,ma;
}tr[8*N];
inline int get(int id,int x){return a[id].k*x+a[id].b;}
inline double gett(int id1,int id2){return (1.0*a[id2].b-a[id1].b)/(1.0*a[id1].k-a[id2].k);}
void build(int id,int l,int r)
{
	tr[id].l=l;tr[id].r=r;tr[id].ma=0;
	if(l==r)return;int mid=(l+r)>>1;
	build(id*2,l,mid);build(id*2+1,mid+1,r);
}
void add(int id,int v)
{	
	int mid=(tr[id].l+tr[id].r)>>1;
	if(!tr[id].ma){tr[id].ma=v;return;}
	int p1=get(tr[id].ma,tr[id].l),p2=get(tr[id].ma,tr[id].r),
		 p3=get(v,tr[id].l),p4=get(v,tr[id].r);
	if(p3<=p1&&p4<=p2)return;
	if(p3>=p1&&p4>=p2){tr[id].ma=v;return;}
	double p=gett(tr[id].ma,v);
	if(p3>=p1)
	{	
		if(p<=mid)add(id*2,v);
		else add(id*2+1,tr[id].ma),tr[id].ma=v;
	}
	else
	{	
		if(p<=mid)add(id*2,tr[id].ma),tr[id].ma=v;
		else add(id*2+1,v);
	}
}
inline int clac(int x,int p1,int p2){if(p1>p2)swap(p1,p2);return (get(p1,x)>=get(p2,x))?p1:p2;}
int getans(int id,int p)
{	
	if(tr[id].l==tr[id].r)return tr[id].ma;
	int mid=(tr[id].l+tr[id].r)>>1;
	if(p<=mid)return clac(p,tr[id].ma,getans(id*2,p));
	else return clac(p,tr[id].ma,getans(id*2+1,p));
}
signed main()	
{
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	cin>>n>>m;build(1,-1e6,1e6);
	for(int i=1;i<=n;i++)scanf("%lld%lld",&pa[i],&pb[i]);
	for(int i=1;i<=m;i++)
	{	
		int pp,k;scanf("%lld%lld",&pp,&k);
		p[pp].push_back((node){i,k});
	}
	for(int i=1;i<=n;i++)suma[i]=pa[i]+suma[i-1],sumb[i]=sumb[i-1]+pb[i];
	for(int i=1;i<=n;i++)
	{	
		for(int j=0;j<p[i].size();j++)
		{		
			int k=p[i][j].k,id=p[i][j].id;
			int an=getans(1,k);
			ans[id]+=suma[i]-k*sumb[i]+get(an,k);
		}
		a[i].k=sumb[i],a[i].b=-suma[i];
		add(1,i);
	}
	memset(suma,0,sizeof(suma));memset(sumb,0,sizeof(sumb));
	for(int i=n;i>=1;i--)suma[i]=suma[i+1]+pa[i],sumb[i]=sumb[i+1]+pb[i];
	build(1,-1e6,1e6);
	for(int i=n;i>=1;i--)
	{
		for(int j=0;j<p[i].size();j++)
		{		
			int k=p[i][j].k,id=p[i][j].id;
			int an=getans(1,k);
			ans[id]+=suma[i]-k*sumb[i]+get(an,k)-(pa[i]-k*pb[i]);
		}
		a[i].k=sumb[i],a[i].b=-suma[i];
		add(1,i);
	}
	for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
	return 0;
}

还是太菜了,无话可说

T4.构树

考虑多项式算法,好像有一个叫做扩展Cayley公式的东西:

被确定边分为大小为\(a_1,a_2,\cdots, a_m\)的连通块,则有\(n^{m-2}\prod {a_i}\)种生成树

显然是不会的,但可以知道结论
这道题本质上是在原来的树中选择一些边留下,剩下的删去,再通过重新连边形成一棵树的方案数
因为要用到这个公式,所以dp中理应设一个联通块大小的东西,这里的定义选择的边把点连成联通块
\(f_{x,i,j}\)表示\(x\)为根的树中,联通块大小为\(i\)至少留下\(j\)条边的方案数
那么转移利用子树合并,有

\[dp_{u,i,j}\cdot dp_{v,k,l}\rightarrow dp_{u,i+k,j+l+1} \]

这个是选边留下的转移,下面是边不留下的转移

\[nk\cdot dp_{u,i,j}\cdot dp_{v,k,l}\rightarrow dp_{u,i,j+l} \]

因为这个公式是随便构的树,因此算的是至少的形式,答案最终要二项式反演
优化一下的话,发现乘上\(k\)的系数可以转化为在\(k\)个里面选1个,所以第二维不记录联通块大小,改成0(不选)1(选)
思路比较神奇,感觉理解也不是很深刻,被称作是套路的转化依然不会用

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=8005;
const int mod=1e9+7;
struct node{	
	int from,to,next;
}a[2*N];
int head[N],mm=1;
inline void add(int x,int y)
{
	a[mm].from=x;a[mm].to=y;
	a[mm].next=head[x];head[x]=mm++;
}
int n,f[N][2][N],g[2][N];
int jc[N],inv[N],jcny[N];
inline void pre()
{
	jc[0]=jc[1]=inv[1]=jcny[0]=jcny[1]=1;
	for(int i=2;i<=n;i++)
	{
		jc[i]=jc[i-1]*i%mod;
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		jcny[i]=jcny[i-1]*inv[i]%mod;
	}	
}
inline int C(int x,int y)
{
	if(x<y)return 0;if(!y)return 1;
	return jc[x]*jcny[y]%mod*jcny[x-y]%mod;
}
bool v[N];int size[N],ans[N],p[N];
void dfs(int x)
{
	v[x]=1;f[x][1][0]=f[x][0][0]=1;size[x]=1;
	for(int i=head[x];i;i=a[i].next)
	{
		int y=a[i].to;if(v[y])continue;
		dfs(y);memset(g,0,sizeof(g));
		for(int j=0;j<size[x];j++)for(int k=0;k<size[y];k++)
		{
			g[1][j+k+1]=(g[1][j+k+1]+f[x][0][j]*f[y][1][k]%mod+f[x][1][j]*f[y][0][k]%mod)%mod;
			g[0][j+k+1]=(g[0][j+k+1]+f[x][0][j]*f[y][0][k]%mod)%mod;
			g[0][j+k]=(g[0][j+k]+f[x][0][j]*f[y][1][k]%mod*n%mod)%mod;
			g[1][j+k]=(g[1][j+k]+f[x][1][j]*f[y][1][k]%mod*n%mod)%mod;
		}
		size[x]+=size[y];
		for(int j=0;j<=1;j++)for(int k=0;k<size[x];k++)f[x][j][k]=g[j][k];
	}
}
signed main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	cin>>n;
	for(int i=1;i<n;i++)
	{		
		int x,y;scanf("%lld%lld",&x,&y);
		add(x,y);add(y,x);
	}
	pre();dfs(1);
	for(int i=0;i<n;i++)p[i]=f[1][1][i]*inv[n]%mod;
	for(int i=0;i<n;i++)
	{	
		for(int j=i;j<n;j++)
		{
			if((j-i)&1)ans[i]=(ans[i]-p[j]*C(j,i)%mod+mod)%mod;
			else ans[i]=(ans[i]+p[j]*C(j,i)%mod)%mod;	
		}
	}	
	for(int i=0;i<n;i++)printf("%lld ",ans[i]);
	return 0;
}

考试总结

没有停止挂分,没有想正解,只能靠暴力度日
然而都是低档暴力基本没用,希望联赛不会是这样
还有四天,最后四场了,希望不留遗憾吧

posted @ 2021-11-16 07:26  D'A'T  阅读(39)  评论(0编辑  收藏  举报