模拟48 考试总结
暑假最后一场
考试经过
开始先想T1,一个多小时想出了正解,后来整了整细节过了三个样例,觉得挺稳就交了,T2不会先开T3,二分之后不会\(check\),想了个不知道多少分的贪心冲上去了,T4最后冲暴力,T2补了一发信仰,然后想着能不能上200
分出来:12+3+0+0=15
输得渣都不剩
T1终究还是挂了,T3真就一分没有,T4又因为返回值RE了
下次再RE我就把电脑屏幕吃掉
心情这事根本说不明白
迷茫 混沌 不知所云.
T1.Lighthouse
考完10分钟A掉了
首先如果没有限制就是\((n-1)!/2\),就是圆排列
看到限制以及20的范围,正难则反直接容斥,枚举子集奇减偶加,枚举选边的情况\(s\),钦定这些边必须选,用并查集维护一下,相当于直接把若干个点强制缩成一个,注意这里由于顺序可以颠倒所以乘2,柿子是
这里\(k\)是选择的点中构成的联通块数量
然后就是恶心的特判
首先一个点如果度数大于2说明构不成环不合法
如果选择点中出现了一个点数小于\(n\)的环也不合法
就完了,然而我因为没用并查集判环就死了
#include <bits/stdc++.h>
using namespace std;
#define R register
const int N=10000050;
const int mod=1e9+7;
const int ny2=500000004;
inline int ksm(int x,int y)
{
int s=1;x%=mod;
for(;y;y>>=1)
{
if(y&1)s=1ll*s*x%mod;
x=1ll*x*x%mod;
}
return s;
}
int jc[N],a[45],lsh[45],tot;
int fa[42],to[41],size[41],mp[N];
inline int find(int x)
{
if(fa[x]!=x)fa[x]=find(fa[x]);
return fa[x];
}
bitset <45> v,vv;int kp[50];
signed main()
{
int n,m;cin>>n>>m;jc[0]=1;
for(int i=1;i<=n+10;i++)jc[i]=1ll*jc[i-1]*i%mod;
for(int i=1;i<=45;i++)kp[i]=ksm(2,i);
for(int i=1;i<=m;i++)scanf("%d%d",&a[i],&a[i+m]);
for(int i=1;i<=2*m;i++)lsh[i]=a[i];
sort(lsh+1,lsh+2*m+1);
int cnt=unique(lsh+1,lsh+2*m+1)-lsh-1;
for(int i=1;i<=2*m;i++)a[i]=lower_bound(lsh+1,lsh+cnt+1,a[i])-lsh;
int ans=1ll*jc[n-1]*ny2%mod;
for(R int i=1;i<(1<<m);i++)
{
int s=0,ss=0,ga=0,p=0;bool stop=0;
memset(to,0,sizeof(to));v.reset();vv.reset();
for(R int j=1;j<=cnt;j++)fa[j]=j,size[j]=1;
for(R int j=1;j<=m;j++)
if((i>>(j-1))&1)
{
s++;int x=a[j],y=a[j+m];
to[x]++,to[y]++;
if(to[x]>2||to[y]>2){stop=1;break;}
int fx=find(x),fy=find(y);
if(fx==fy&&size[fx]!=n){stop=1;break;}
fa[fx]=fy;size[fy]+=size[fx],vv[fy]=1;
}
if(stop)continue;
int mu=0;
for(R int j=1;j<=cnt;j++)
{
int f=find(j);
if(!v[f]){v[f]=1,ss++;if(vv[f])mu++;}
}
ss=cnt-ss;
int sum=1ll*jc[n-ss-1]*ny2%mod*kp[mu]%mod;
if(s&1)ans=(1ll*ans-sum+mod)%mod;
else ans=(1ll*ans+sum)%mod;
}
cout<<ans<<endl;
return 0;
}
T2.Miner
欧拉路
答案其实不难,是
\(k\)是联通块数量,\(c\)是每个连通块奇点个数
挺好理解的,两个奇点一笔画
构造方案就是在每个连通块中给每一个奇点与另外的一个奇点连边,注意是配对不是乱连,只会出来\(c/2\)条边,然后跑欧拉路就行
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=100050;
struct node{
int from,to,next;
}a[4*N];
int head[4*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 du[N],p[N],mp[N];bool v[4*N];
int dfs(int x,int pp)
{
v[x]=1;p[x]=pp;int ans=(du[x]&1);
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].to;
if(v[y])continue;
ans+=dfs(y,pp);
}
return ans;
}
vector <int> f[N],an;
stack <int> st;
stack <pair<int,int> >cpu;
void dfss(int r)
{
cpu.push(make_pair(r,0));
while(cpu.size())
{
int x=cpu.top().first,vv=cpu.top().second,i=head[x];
while(i&&v[i])i=a[i].next;
if(i)
{
int y=a[i].to;
cpu.push(make_pair(y,1));
v[i]=v[i^1]=1;
head[x]=a[i].next;
}
else
{if(vv)st.push(x);cpu.pop();}
}
}/**/
/*void dfss(int x)
{
for(int i=head[x];i;head[x]=i=a[i].next)
{
int y=a[i].to;
if(v[i])continue;
v[i]=v[i^1]=1;
dfss(y);st.push(y);
}
}*/
signed main()
{
int n,m;cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y;scanf("%lld%lld",&x,&y);
add(x,y);add(y,x);
du[x]++;du[y]++;
}
int ans=0,cnt=0;
for(int i=1;i<=n;i++)
if((!v[i])&&du[i])ans+=max((int)1,dfs(i,++cnt)/2),mp[cnt]=i;
printf("%lld\n",ans-1);
for(int i=1;i<=n;i++)if(du[i]&1)f[p[i]].push_back(i);
memset(v,0,sizeof(v));
int num=n;
for(int i=1;i<=cnt;i++)
{
num++;
for(int j=3;j<f[i].size();j+=2)
{
int p1=f[i][j-1],p2=f[i][j];
add(p1,num);add(num,p1);
add(p2,num);add(num,p2);
}
if(f[i].size()&1)
{
int ga=f[i][f[i].size()-1];
add(ga,num);add(num,ga);
}
int ga=f[i].size()?f[i][0]:mp[i];dfss(ga);
an.clear();
while(st.size())an.push_back(st.top()),st.pop();
if(i==1)printf("%lld\n",ga);
else printf("1 %lld\n",ga);
for(int j=0;j<an.size();j++)
{
if(an[j]==num)continue;
if(j!=0&&an[j-1]==num)printf("1 %lld\n",an[j]);
else printf("0 %lld\n",an[j]);
}
}
return 0;
}
连边我建了虚点,方便记录答案
由于这个算法有很深的递归,所以模拟栈实现
然而一直不对,发现模拟的时候第一个点是不能记录答案的,所以判掉
可以比对一下真正的dfs和模拟的,调了好久
T3.Lyk Love painting
一眼二分,判断用dp搞
首先如果有格子数字大于当前的二分值直接判断不合法
设\(f_i\)表示盖满前\(i\)个格子最少用的画数量
转移之前预处理出三个数组\(f_1,f_2,f_3\),具体表示当前的一个格子如果只在第一行放画,第二行放画,一二行放画最远保证合法放的位置,建议看代码
转移首先用\(f_i=f_{f3_i}+1\),然后用两个指针跳一下,同样看代码
复杂度\(nmlogS\)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=100050;
int a[3][N],s1[N],s2[N],s3[N];
int f1[N],f2[N],f3[N],f[N];
int n,m;
inline void gan(int x)
{
f1[0]=f2[0]=f3[0]=0;
for(int i=1;i<=n;i++)
{
int l=0,r=i,ans;
while(l<=r)
{
int mid=(l+r)>>1;
if(s1[i]-s1[mid]<=x)ans=mid,r=mid-1;
else l=mid+1;
}
f1[i]=ans;l=0;r=i;
while(l<=r)
{
int mid=(l+r)>>1;
if(s2[i]-s2[mid]<=x)ans=mid,r=mid-1;
else l=mid+1;
}
f2[i]=ans;l=0;r=i;
while(l<=r)
{
int mid=(l+r)>>1;
if(s3[i]-s3[mid]<=x)ans=mid,r=mid-1;
else l=mid+1;
}
f3[i]=ans;
}
}
inline bool check(int x)
{
for(int i=1;i<=n;i++)
if(a[1][i]>x||a[2][i]>x)return 0;
gan(x);memset(f,0x3f,sizeof(f));f[0]=0;
for(int i=1;i<=n;i++)
{
f[i]=f[f3[i]]+1;
int l1=i,l2=i,sum=0;
while(sum<m&&(l1||l2))
{
if(l1>=l2)l1=f1[l1];
else l2=f2[l2];
sum++;
f[i]=min(f[i],f[max(l1,l2)]+sum);
}
}
if(f[n]<=m)return 1;
return 0;
}
inline int er(int l,int r)
{
if(l>=r)return r;
int mid=(l+r)>>1;
if(check(mid))return er(l,mid);
else return er(mid+1,r);
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=2;i++)
for(int j=1;j<=n;j++)
scanf("%lld",&a[i][j]);
for(int i=1;i<=n;i++)
{
s1[i]=s1[i-1]+a[1][i];
s2[i]=s2[i-1]+a[2][i];
s3[i]=s3[i-1]+a[1][i]+a[2][i];
}
cout<<er(1,1e12);
return 0;
}
T4.Revive
先化减柿子然后搞数据结构,咕了
考试总结
任何时候不要有“我能AC”之类的错觉
事实上是个暴力都比你分高
时间啊 真的不多了

浙公网安备 33010602011771号