模拟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没开可惜了
把题看清楚,学会转化问题

浙公网安备 33010602011771号