noip模拟48[脑袋好像有问题了]
noip模拟48 solutions
我觉得我真是SB了,我也能有一气挂他个100分的时候
不知道为啥,我取完模之后直接>>=1
咱也不知道咋想的,然后100->17
害以后还是少干这种完蛋的事吧
T1 Lighthouse
这个他们都说是普通容斥,还有说子集反演的
我就觉得是二项式反演,yyds
设\(f_i\)表示至少加入了i条不合法的边的方案数,我们最后求的就是\(g_0\)
直接上反演就行了,化简之后就是偶加奇减
这个f求得时候不能直接圆排列,还要考虑不合法的情况
一个点上连了三条边,肯定不能构成环
里面出现了一个小环,也不合法,但是如果这个环就是n个点的环的话是合法的
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e7+5;
const int M=205;
const ll mod=1e9+7;
int n,m;
int fr[M],to[M];
ll f[M],jc[N],tc[N],ans;
ll mst[N],mnt;
int vis[N];
ll ksm(ll x,ll y){
ll ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;
y>>=1;
}
return ret;
}
signed main(){
scanf("%d%d",&n,&m);mnt=0;
for(re i=1;i<=m;i++)scanf("%d%d",&fr[i],&to[i]);
jc[0]=1;for(re i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod;
tc[0]=1;for(re i=1;i<=n;i++)tc[i]=tc[i-1]*2%mod;
for(re s=0;s<(1<<m);s++){
int sp=0,se=0,flag=1;
for(re i=1;i<=m;i++){
if(!((s>>i-1)&1))continue;se++;
if(vis[fr[i]]==2||vis[to[i]]==2){flag=0;break;}
if(!vis[fr[i]])sp++;vis[fr[i]]++;
if(!vis[to[i]])sp++;vis[to[i]]++;
}//cout<<flag<<" ";
for(re i=1;i<=mnt;i++)if((s&mst[i])==mst[i])flag=0;
if((sp==se)&&(sp!=n)&&(flag)&&(s)){mst[++mnt]=s;flag=0;}
if((sp==se)&&(sp==n)&&(flag)&&(s)){
mst[++mnt]=s;flag=0;
f[n]=(f[n]+2)%mod;
}
for(re i=1;i<=m;i++){
if(!((s>>i-1)&1))continue;
vis[fr[i]]=0;vis[to[i]]=0;
}
if(!flag)continue;
f[se]=(f[se]+jc[n-se-1]*tc[sp-se])%mod;
}
//cout<<mnt<<endl;
/*for(re i=1;i<=mnt;i++){
for(re j=1;j<=m;j++)
if((mst[i]>>j-1)&1)cout<<"1";
else cout<<"0";
cout<<endl;
}*/
//for(re i=0;i<=m;i++)cout<<f[i]<<" ";cout<<endl;
for(re i=0;i<=m;i++)f[i]=f[i]*ksm(2,mod-2)%mod;//cout<<f[i]<<" ";cout<<endl;
for(re i=0,xs=1;i<=m;i++,xs=-xs)ans=(ans+xs*f[i]+mod)%mod;
printf("%lld",ans);
}
T2 Miner
我真的没看出来这个是欧拉路,写的很隐蔽啊
我们要不重不漏的经过每一条边,
就像题解说的,我们找到这个图中的每一个联通块
我们要用一些边把这些块串联起来
每个块内只可能有偶数个奇数度数的点,我们每连一条边就会少两个奇数的点
我们就直接给每个块的奇数的个数/2,设个数为\(c_i\)
\[\sum\limits\max(1,\frac{c_i}{2})-1
\]
减一就是因为我们可以留下两个奇数的点,不是回路,而是欧拉路
然后我们随便调出几个点先全部串起来,再瞎连就行了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
const int M=1e5+5;
int n,m;
int to[M*10],nxt[M*10],head[N],rp=1,val[M*10];
void add_edg(int x,int y,int z){
to[++rp]=y;
val[rp]=z;
nxt[rp]=head[x];
head[x]=rp;
}
int c[N],cc,du[N];
bool vis[N];
vector<int> vec[N];
int ji[N],cnt;
void dfs(int x){
vis[x]=true;
if(du[x]&1){
c[cc]++;
vec[cc].push_back(x);
}
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(vis[y])continue;
dfs(y);
}
}
struct node{
int go,ho;
node(){}
node(int x,int y){go=x;ho=y;}
}sta[M*10],ans[M*10];
int top,an,ans1;
bool via[M*10];
void oular(){
sta[++top]=node(vec[1][1],0);
while(top>0){
//cout<<top<<" "<<sta[top].go<<endl;
int x=sta[top].go,i=head[x];
while(i&&via[i])i=nxt[i];
if(i){
sta[++top]=node(to[i],val[i]);
via[i]=via[i^1]=true;
head[x]=nxt[i];
}
else{
ans[++an]=sta[top];top--;
}
}
}
signed main(){
scanf("%d%d",&n,&m);
for(re i=1,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
add_edg(x,y,0);add_edg(y,x,0);
du[x]++;du[y]++;
}
for(re i=1;i<=n;i++)
if(!vis[i]&&head[i]){
cc++;vec[cc].push_back(0);dfs(i);
if(c[cc]==0)vec[cc].push_back(i),vec[cc].push_back(i);
ans1+=max(c[cc]/2,1);
}
//cout<<cc<<endl;
for(re j=3;j<=c[1];j++)ji[++cnt]=vec[1][j];
for(re i=2;i<=cc;i++){
add_edg(vec[i-1][2],vec[i][1],1);
add_edg(vec[i][1],vec[i-1][2],1);
//cout<<vec[i-1][2]<<" "<<vec[i][1]<<endl;
for(re j=3;j<=c[i];j++)ji[++cnt]=vec[i][j];
}
//cout<<cnt<<endl;
for(re i=2;i<=cnt;i+=2){
add_edg(ji[i-1],ji[i],1);
add_edg(ji[i],ji[i-1],1);
}
printf("%d\n",ans1-1);
oular();
printf("%d\n",ans[an].go);
for(re i=an-1;i>=1;i--)printf("%d %d\n",ans[i].ho,ans[i].go);
}
T3 Lyk Love painting
这个dp我考场上想都没想直接弃了,没想到这么简单
基础dp就不说了
一看是最大值最小,那肯定是二分了
我们用dp判断是否合法
当前二分值就是限制,我们直接按照这个限制向后转移,
分别转移上面的,下面的,上下一起的
上下一起的直接找到距离当前位置最远的合法的点
上面和下面的直接跳着往前走,因为越靠前越优,大的向前跳
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e5+5;
const int M=105;
ll n,m;
ll fail[N][4],pre[N][4],dp[N];
bool check(ll lim){
fail[0][1]=fail[0][2]=fail[0][3]=0;
for(re i=1;i<=n;i++)
for(re j=1;j<=3;j++){
fail[i][j]=fail[i-1][j];
while(pre[i][j]-pre[fail[i][j]][j]>lim)fail[i][j]++;
}
memset(dp,0x3f,sizeof(dp));dp[0]=0;
for(re i=1;i<=n;i++){
dp[i]=min(dp[i],dp[fail[i][3]]+1);
ll i1=i,i2=i,no=0;
while(no+1<=m&&(i1||i2)){
if(i1<i2)i2=fail[i2][2];
else i1=fail[i1][1];no++;
dp[i]=min(dp[i],dp[max(i1,i2)]+no);
}
if(dp[i]>m)return false;
}
return true;
}
signed main(){
scanf("%lld%lld",&n,&m);
for(re i=1;i<=n;i++){ll x;scanf("%lld",&x),pre[i][1]=x,pre[i][3]+=x;}
for(re i=1;i<=n;i++){ll x;scanf("%lld",&x),pre[i][2]=x,pre[i][3]+=x;}
for(re i=1;i<=n;i++)for(re j=1;j<=3;j++)pre[i][j]+=pre[i-1][j];
ll l=0,r=pre[n][3],mid;
while(l<r){
mid=l+r>>1;//cout<<l<<" "<<r<<" "<<mid<<endl;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%lld",l);
}
T4 Revive
颓废状态,暂放
QQ:2953174821