模拟63 考试总结

也许奇迹会在一瞬间出现吧

考试经过

开题T1到T4都不很可做,于是先开T1。。。
越看越觉得不太对劲,开始大力分析性质,分析到最后貌似出来了,但不太会维护
先写了个暴力交上去,然后觉得还是可以再莽一下,想到可以启发式就开写
中间假了好几次,最后调完了,拍上发现WA,手模一通发现暴力假了。。。
然后失去梦想,后三题直接开暴力,T3一个裸的前缀和我甚至找不到多项式做法。。。
100+15+8+20=143,rk2
然后发现T3有多么可做。。。战神tql

T1.电压机制

题意就是给一个图黑白染色,有且仅有一条边两边颜色相同,问总共方案
可以先dfs一遍跑出生成树,然后考虑分别树边和非树边是否可以被选,没模数所以答案可以直接统计
非树边分成两类,两点颜色相同的边为一类边,否则为二类边,这个可以用dfs树的深度奇偶来判
当且仅当只有一条一类边时贡献会加一,只有这样才能选这条边
接着考虑树边,我们要把他断开,之后原来的树会变成两棵,一部分非树边连在两树之间,一部分连在树自己里面
那么此时当且仅当两树之间的边都为一类边,各自树之间的边均为二类边,该方案合法
思考怎么维护,dfs的时候用set存子树到外部的一类,二类边,合并时启发式,并把过时的删除
具体的就是对于每条边,设深度小的为\(x\),深度大的为\(y\),在\(y\)set里存上\(x\),检查合法就是判断一类集合的元素个数等于一类边的总数,二类集合为空
复杂度貌似是俩\(log\),然而题解用了差分\(O(n)\)爆踩我
好久没考场过题了,顺便绿框,还是要发下码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=200050;
struct node{
	int from,to,next,op;
}a[2*N];
int head[N],mm=2;
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,m,ss1,ss2;
int d[N],id[N];bool v[N];
void dfs(int x)
{
	v[x]=1;
	for(int i=head[x];i;i=a[i].next)	
	{
		int y=a[i].to;
		if(v[y])continue;
		a[i].op=a[i^1].op=1;
		d[y]=d[x]+1;
		dfs(y);
	}
}
multiset <int> s1[N],s2[N];
vector <int> tmp1[N],tmp2[N];
int gan(int x,int fa)
{
	int son1=0,son2=0;
	int ans=0;
	for(int i=head[x];i;i=a[i].next)
	{
		if(!a[i].op)continue;
		int y=a[i].to;
		if(y==fa)continue;
		ans+=gan(y,x);
		if(s1[y].size()>s1[son1].size())son1=y;
		if(s2[y].size()>s2[son2].size())son2=y;
	}
	if(x==1)return ans;
	if(son1)swap(s1[son1],s1[x]);
	if(son2)swap(s2[son2],s2[x]);
	for(int i=head[x];i;i=a[i].next)
	{	
		if(!a[i].op)continue;
		int y=a[i].to;
		if(y==fa)continue;
		if(y!=son1)for(auto it=s1[y].begin();it!=s1[y].end();it++)s1[x].insert(*it);
		if(y!=son2)for(auto it=s2[y].begin();it!=s2[y].end();it++)s2[x].insert(*it);
	}
	for(int i=0;i<tmp1[x].size();i++)s1[x].insert(tmp1[x][i]);
	for(int i=0;i<tmp2[x].size();i++)s2[x].insert(tmp2[x][i]);
	s1[x].erase(x);s2[x].erase(x);
	if(s1[x].size()==ss1&&s2[x].empty())return ans+1;
	else return ans;
}
signed main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=m;i++)	
	{
		int x,y;scanf("%lld%lld",&x,&y);
		add(x,y);add(y,x);
	}
	d[1]=1;dfs(1);
	int ans=0;
	for(int i=2;i<mm;i+=2)
	{
		if(a[i].op)continue;
		int x=a[i].from,y=a[i].to;
		if(d[x]<d[y])swap(x,y);
		if((d[x]-d[y])&1)ss2++,tmp2[x].push_back(y);
		else ss1++,tmp1[x].push_back(y);
		if(d[x]==d[y])tmp2[y].push_back(x);
	}
	if(ss1==1)ans++;
	cout<<ans+gan(1,0)<<endl;
	return 0;
}

T2.括号密码

不太会,咕了

T3.排列

原来是码农题
暴力是\(n^4\)的,我是智障,对一个前缀和轻松\(n^3\)
可以把下标作为第一位,值域作为第二维,用二维前缀和,每次枚举两个数,保证另外两个不连续,直接求就行
然后你发现24种里有22种都能这么做,所以你只要写个22种情况的大力分类讨论就有91超高分。。。
然而2413和3142不能这么做,因为你找不到这种选数方案,只能暴力
那么一般遇到这种遇到限制枚举就可以考虑树状数组了
2413为例,每次从后往前枚举3,然后枚举4,值域上开两个树状数组维护,那么就是找一个数对,左边的比右边大,求总数
每次移动一位的贡献是可以维护的,所以每次只要维护他多了、少了多少就行,左边删,右边加,变量维护,每次加答案
前方高能,代码极为壮观

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2050;
int a[N],p1,p2,p3,p4,n;
int b[N][N],sum[N][N],ans;
inline int get(int xl,int xr,int yl,int yr)
{
	if(xl>xr||yl>yr)return 0;
	return sum[xr][yr]-sum[xl-1][yr]-sum[xr][yl-1]+sum[xl-1][yl-1];
}
inline void gan1(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(1,i-1,1,a[i]-1)*get(j+1,n,a[j]+1,n);}
inline void gan2(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(1,i-1,1,a[i]-1)*get(j+1,n,a[i]+1,a[j]-1);}
inline void gan3(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(1,i-1,1,a[j]-1)*get(j+1,n,a[i]+1,n);}
inline void gan4(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(1,i-1,1,a[j]-1)*get(i+1,j-1,a[i]+1,n);}
inline void gan5(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(1,i-1,1,a[j]-1)*get(j+1,n,a[j]+1,a[i]-1);}
inline void gan6(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(i+1,j-1,a[j]+1,n)*get(j+1,n,a[i]+1,a[j]-1);}
inline void gan7(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(i+1,j-1,1,a[i]-1)*get(j+1,n,a[j]+1,n);}
inline void gan8(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(i+1,j-1,1,a[i]-1)*get(j+1,n,a[i]+1,a[j]-1);}
inline void gan9(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(1,i-1,a[j]+1,a[i]-1)*get(j+1,n,a[i]+1,n);}
inline void gan10(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(i+1,j-1,a[i]+1,a[j]-1)*get(j+1,n,1,a[i]-1);}
inline void gan12(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(i+1,j-1,a[j]+1,n)*get(j+1,n,1,a[i]-1);}
inline void gan13(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(i+1,j-1,1,a[j]-1)*get(j+1,n,a[i]+1,n);}
inline void gan15(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(i+1,j-1,a[j]+1,a[i]-1)*get(j+1,n,a[i]+1,n);}
inline void gan16(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(1,i-1,a[i]+1,a[j]-1)*get(j+1,n,1,a[i]-1);}
inline void gan17(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(i+1,j-1,a[i]+1,n)*get(j+1,n,a[j]+1,a[i]-1);}
inline void gan18(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(i+1,j-1,a[i]+1,n)*get(j+1,n,1,a[j]-1);}
inline void gan19(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(i+1,j-1,1,a[j]-1)*get(j+1,n,a[j]+1,a[i]-1);}
inline void gan20(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(1,i-1,a[j]+1,n)*get(j+1,n,a[i]+1,a[j]-1);}
inline void gan21(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(1,i-1,a[j]+1,n)*get(i+1,j-1,1,a[i]-1);}
inline void gan22(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]<a[j])ans+=get(1,i-1,a[j]+1,n)*get(j+1,n,1,a[i]-1);}
inline void gan23(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(1,i-1,a[i]+1,n)*get(j+1,n,a[j]+1,a[i]-1);}
inline void gan24(){for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i]>a[j])ans+=get(1,i-1,a[i]+1,n)*get(j+1,n,1,a[j]-1);}
struct bit{
	int c[N];
	inline int lowbit(int x){return x&(-x);}
	inline void add(int x,int v){for(int i=x;i<=n;i+=lowbit(i))c[i]+=v;}
	inline int getsum(int p){int s=0;for(int i=p;i;i-=lowbit(i))s+=c[i];return s;}
	inline int get(int l,int r){if(l>r)return 0;return getsum(r)-getsum(l-1);}
}tr1,tr2;
inline void gan11()
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<i;j++)tr2.add(a[j],1);
		int an=0;
		for(int j=1;j<i;j++)
		{
			tr2.add(a[j],-1);
			an-=tr1.get(a[j]+1,a[i]-1);
			if(a[j]>a[i])ans+=an;
			tr1.add(a[j],1);
			if(a[j]<a[i])an+=tr2.get(1,a[j]);
		}
		for(int j=1;j<i;j++)tr1.add(a[j],-1);
	}
}
inline void gan14(){reverse(a+1,a+1+n);gan11();}
signed main()
{	
	freopen("c.in","r",stdin);freopen("c.out","w",stdout);
	cin>>n>>p1>>p2>>p3>>p4;
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);for(int i=1;i<=n;i++)b[i][a[i]]=1;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+b[i][j];
	if(p1==1&&p2==2&&p3==3&&p4==4)gan1();if(p1==1&&p2==2&&p3==4&&p4==3)gan2();if(p1==1&&p2==3&&p3==2&&p4==4)gan3();
	if(p1==1&&p2==3&&p3==4&&p4==2)gan4();if(p1==1&&p2==4&&p3==2&&p4==3)gan5();if(p1==1&&p2==4&&p3==3&&p4==2)gan6();
	if(p1==2&&p2==1&&p3==3&&p4==4)gan7();if(p1==2&&p2==1&&p3==4&&p4==3)gan8();if(p1==2&&p2==3&&p3==1&&p4==4)gan9();
	if(p1==2&&p2==3&&p3==4&&p4==1)gan10();if(p1==2&&p2==4&&p3==1&&p4==3)gan11();if(p1==2&&p2==4&&p3==3&&p4==1)gan12();
	if(p1==3&&p2==1&&p3==2&&p4==4)gan13();if(p1==3&&p2==1&&p3==4&&p4==2)gan14();if(p1==3&&p2==2&&p3==1&&p4==4)gan15();
	if(p1==3&&p2==2&&p3==4&&p4==1)gan16();if(p1==3&&p2==4&&p3==1&&p4==2)gan17();if(p1==3&&p2==4&&p3==2&&p4==1)gan18();
	if(p1==4&&p2==1&&p3==2&&p4==3)gan19();if(p1==4&&p2==1&&p3==3&&p4==2)gan20();if(p1==4&&p2==2&&p3==1&&p4==3)gan21();
	if(p1==4&&p2==2&&p3==3&&p4==1)gan22();if(p1==4&&p2==3&&p3==1&&p4==2)gan23();if(p1==4&&p2==3&&p3==2&&p4==1)gan24();
	cout<<ans<<endl;return 0;
}

神 清 气 爽

T4.B关系

神仙题,根本不会

考试总结

策略还是比较正确的,毕竟有正解就和暴力差好多,思路已经很清晰了可以适当花时间打下正解,前提保证不假
感觉后面还能再拿不少分,T3没开可惜了
把题看清楚,学会转化问题

posted @ 2021-09-30 09:36  D'A'T  阅读(52)  评论(0)    收藏  举报