NOI模拟19
网格图我用的spfa,然后寄了,不对,还有建议直接建边,要不然每次上下左右找的话常数太大了!!
第一题似乎是个nb题,于是我开始想\(meet\ in\ the\ middle\),于是搞到了60,正解是个傻逼做法
后面两个只会打最短路辽~~
T1 书
关于这个傻逼正解我就不多说啥了,我来说一下我的折半状压
发现有用的对只有20个,所以我们压这样的对是否可以在之后出现,然后用一些奇妙的办法,可以将复杂度降至 \(\mathcal{O(2^{\frac{n}{2}}n^3)}\)
于是这个做法极其难写又难调,还是写正解吧,正解就是傻逼dp,nnd
折半dp
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=1<<20;
const int mod=1e9+7;
int n,a[45];char b[45];
int dp[2][N][41],ans;
bool ok(int x){return !(x&1);}
signed main(){
freopen("book.in","r",stdin);
freopen("book.out","w",stdout);
// cerr<<(sizeof(dp)>>20)<<endl;
n=read();scanf("%s",b+1);
fo(i,1,n-1)a[i]=b[i]-'0';
int u=(1<<(n>>1))-1;
int now=0;dp[now][0][0]=1;
fo(o,0,n-1){
now^=1;
int nt=o+1;while(a[nt]==1)nt++;
int len=nt-o;
fo(s,0,u){
int cnt=0;
fo(i,1,n>>1)if(!(s>>i-1&1)){
cnt+=2;
if(ok(i)&&(!(s>>((i>>1)-1)&1)))cnt--;
}
cnt=n-cnt-o;
if(len==1){
fo(i,0,n)if(dp[now^1][s][i]){
if(i&&((i*2<=n&&!(s>>i-1&1))||(ok(i)&&!(s>>(i>>1)-1&1)))){cnt++;}
int t=s;if(i)t|=((i*2<=n)?(1<<i-1):0)|(ok(i)?(1<<((i>>1)-1)):0);
dp[now][t][0]=(dp[now][t][0]+1ll*dp[now^1][s][i]*cnt)%mod;
fo(j,1,n>>1){
if(i!=j&&i*2!=j&&j*2!=i&&(!(s>>j-1&1)||(ok(j)&&!(s>>((j>>1)-1)&1)))){
dp[now][t][j]=(dp[now][t][j]+dp[now^1][s][i])%mod;
}
if(j*4>n&&i!=j&&i!=j*2&&(!(s>>(j-1)&1))){
dp[now][t][j*2]=(dp[now][t][j*2]+dp[now^1][s][i])%mod;
}
}
if(i&&((i*2<=n&&!(s>>i-1&1))||(ok(i)&&!(s>>(i>>1)-1&1)))){cnt--;}
dp[now^1][s][i]=0;
}
}
else {
fo(i,0,n)if(dp[now^1][s][i]){
fo(j,1,n/(1<<len-1))if(i!=j){
bool flag=true;if(j*(1<<len-1)==i)continue;
fo(k,0,len-2)if(j*(1<<k)==i||(s>>(j*(1<<k))-1)&1){flag=false;break;}
if(!flag)continue;
if(i*2!=j&&j*2!=i){
int t=s;if(i)t|=((i*2<=n)?(1<<i-1):0)|(ok(i)?(1<<((i>>1)-1)):0);
if(ok(j))t|=(1<<((j>>1)-1));
fo(k,0,len-2)t|=(1<<(j*(1<<k)-1));
if(j*(1<<len-1)*2<=n)dp[now][t][j*(1<<len-1)]=(dp[now][t][j*(1<<len-1)]+dp[now^1][s][i])%mod;
else dp[now][t][0]=(dp[now][t][0]+dp[now^1][s][i])%mod;
}
if(j*(1<<len-1)*2!=i&&j*(1<<len-1)!=i*2){
int t=s;if(i)t|=((i*2<=n)?(1<<i-1):0)|(ok(i)?(1<<((i>>1)-1)):0);
fo(k,0,len-1)if(j*(1<<k)*2<=n)t|=(1<<(j*(1<<k)-1));
if(ok(j)&&!(s>>(j>>1)-1&1))dp[now][t][j]=(dp[now][t][j]+dp[now^1][s][i])%mod;
else dp[now][t][0]=(dp[now][t][0]+dp[now^1][s][i])%mod;
}
}
dp[now^1][s][i]=0;
}
}
}
o=nt-1;
}
fo(s,0,u)fo(i,0,n)if(dp[now][s][i])ans=(ans+dp[now][s][i])%mod;
printf("%d\n",ans);
return 0;
}
AC_code(傻逼dp)
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=45;
const int mod=1e9+7;
struct DP{
int a[7];bool fl;
DP(){memset(a,0,sizeof(a));fl=false;}
void init(){memset(a,0,sizeof(a));fl=false;}
bool operator < (DP x)const{
fo(i,0,6)if(a[i]!=x.a[i])return a[i]<x.a[i];
return fl<x.fl;
}
};
map<DP,int> mp[N];
int n,tp[N],ans;char ch[N];
signed main(){
freopen("book.in","r",stdin);
freopen("book.out","w",stdout);
n=read();scanf("%s",ch+1);
DP sta;fo(i,1,n)if(i&1){
int nw=i,ct=0;
while(nw<=n)ct++,nw*=2;
if(ct>1)sta.a[ct-2]++;
}mp[0][sta]=1;int lst=0;
fo(o,0,n-1){
int to=o+1;while(ch[to]=='1')to++;
int len=to-o;
for(pair<DP,int> x:mp[o]){
DP s=x.first;int v=x.second,sm;
sm=0;fo(i,2,6)tp[i]=s.a[i-2],sm+=s.a[i-2]*i;
tp[1]=n-o-sm;tp[7]=s.a[5];tp[8]=s.a[6];
fo(i,len,6)if(tp[i])fo(j,1,i-len+1){
int xsz=tp[i],xsf=tp[i];DP t;
if((!s.fl||lst==1)&&i==tp[8]&&j==1)xsz--;
if((s.fl||lst==1)&&i==tp[7]&&j==i-len+1){xsf--;if(len==1)xsz--;}
fo(i,2,6)t.a[i-2]=tp[i];
if(i>1)t.a[i-2]--;if(j-1>1)t.a[j-1-2]++;if(i-len+1-j>1)t.a[i-len+1-j-2]++;
t.a[5]=j-1;t.a[6]=i-len+1-j;
t.fl=0;mp[to][t]=(mp[to][t]+v*xsz)%mod;
if(len!=1)t.fl=1,mp[to][t]=(mp[to][t]+v*xsf)%mod;
}
}
lst=len;o=to-1;
}
for(pair<DP,int> x:mp[n])ans=(ans+x.second)%mod;
printf("%lld\n",ans);
}
T2 鼠
发现n非常的小,似乎和noi_online的最后一个题非常的像....并没有什么鸟关系
考场上只会最短路,发现可以将贡献拆成两部分,于是可以分治,每次从分治中心向两侧跑最短路,在中间合并
合并的过程是个二维数点,每次钦定谁是最小值就容易转化了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=1e5+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f3f3f3f3f;
struct BIT{
int tr1[3*N],tr2[3*N],n;
void init(int x){n=x;fo(i,1,n)tr1[i]=tr2[i]=0;}
void ins(int x,int v){
v%=mod;
for(int i=x;i<=n;i+=(i&-i)){
tr2[i]=(tr2[i]+v)%mod;
tr1[i]=(tr1[i]+1)%mod;
}
}
pair<int,int> qry(int x){
int ret1=0,ret2=0;
for(int i=x;i;i-=(i&-i)){
ret1=(ret1+tr1[i])%mod;
ret2=(ret2+tr2[i])%mod;
}
return {ret1,ret2};
}
}bit;
int mx[4]={0,0,1,-1};
int my[4]={1,-1,0,0};
vector<pair<int,int>> e[3*N];
int n,a[4][N],v[3*N],id[4][N],ans;
inline bool jd(int x,int y){return x&&x<=3&&y&&y<=n;}
struct DS{
int i,d;
bool operator < (DS a)const{
return d>a.d;
}
};
priority_queue<DS> q;
int dis[4][3*N];bool vis[3*N];
void DIJ(int l,int r,int id,int tp){
fo(i,l,r)fo(j,1,3)dis[tp][(i-1)*3+j]=inf,vis[(i-1)*3+j]=false;
dis[tp][id]=v[id];q.push(DS{id,dis[tp][id]});
while(!q.empty()){
int x=q.top().i;q.pop();
// cerr<<x<<" "<<vis[x]<<" "<<dis[tp][x]<<endl;
if(vis[x])continue;vis[x]=true;
for(pair<int,int> y:e[x]){
if(y.first<=(l-1)*3||y.first>r*3)continue;
// cerr<<y<<endl;
if(dis[tp][y.first]<=dis[tp][x]+y.second)continue;
dis[tp][y.first]=dis[tp][x]+y.second;
q.push(DS{y.first,dis[tp][y.first]});
}
}
}
struct node{
int a,b,id;
bool operator < (node x)const{
if(a!=x.a)return a<x.a;
return b<x.b;
}
bool operator == (node x)const{return a==x.a&&b==x.b;}
}le[3*N],ri[3*N];
int lsh[3*N],lh,ll[N],rr[N];
void sol(int l,int r){
// cerr<<l<<" "<<r<<endl;
if(l==r){
ans=(ans+a[1][l]+a[2][l]*2+a[3][l]+min(ll[l],rr[l]))%mod;
// cerr<<l<<" "<<a[1][l]<<" "<<a[2][l]<<" "<<a[3][l]<<" "<<ans<<endl;
return ;
}
int mid=l+r>>1,sl=(mid-l+1)*3,sr=(r-mid)*3;
sol(l,mid);sol(mid+1,r);
fo(i,1,3)fo(j,l,r)e[id[i][j]].clear();
fo(i,1,3)fo(j,l,mid){
fo(k,0,3){
int x=i+mx[k],y=j+my[k];
if(!(x&&x<=3&&y>=l&&y<=mid))continue;
e[id[i][j]].push_back({id[x][y],v[id[x][y]]});
}
}
e[id[1][l]].push_back({id[3][l],ll[l]-v[id[1][l]]});
e[id[3][l]].push_back({id[1][l],ll[l]-v[id[3][l]]});
fo(i,1,3)fo(j,mid+1,r){
fo(k,0,3){
int x=i+mx[k],y=j+my[k];
if(!(x&&x<=3&&y>mid&&y<=r))continue;
e[id[i][j]].push_back({id[x][y],v[id[x][y]]});
}
}
e[id[1][r]].push_back({id[3][r],rr[r]-v[id[1][r]]});
e[id[3][r]].push_back({id[1][r],rr[r]-v[id[3][r]]});
fo(i,1,3)DIJ(l,mid,id[i][mid],i);
fo(i,1,3)DIJ(mid+1,r,id[i][mid+1],i);
// if(l==1&&r==2){
// fo(i,1,sl)cerr<<i+(l-1)*3<<" "<<dis[1][i+(l-1)*3]<<endl;
// fo(i,1,sr)cerr<<i+mid*3<<" "<<dis[1][i+mid*3]<<endl;
// }
// cerr<<ans<<endl;
// cerr<<"SB1"<<endl;
fo(i,1,sl)le[i]=node{dis[2][i+(l-1)*3]-dis[1][i+(l-1)*3],dis[3][i+(l-1)*3]-dis[1][i+(l-1)*3],i+(l-1)*3};
fo(i,1,sr)ri[i]=node{dis[1][i+mid*3]-dis[2][i+mid*3],dis[1][i+mid*3]-dis[3][i+mid*3],i+mid*3};
lh=0;fo(i,1,sl)lsh[++lh]=le[i].b;fo(i,1,sr)lsh[++lh]=ri[i].b;
sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;bit.init(lh);
sort(le+1,le+sl+1);sort(ri+1,ri+sr+1);int ir=1;
// cerr<<"ZZ"<<endl;
fo(i,1,sl)le[i].b=lower_bound(lsh+1,lsh+lh+1,le[i].b)-lsh;
fo(i,1,sr)ri[i].b=lower_bound(lsh+1,lsh+lh+1,ri[i].b)-lsh;
// cerr<<"FK"<<endl;
fo(i,1,sl){
while(ir<=sr&&ri[ir].a<=le[i].a){
bit.ins(ri[ir].b,dis[1][ri[ir].id]%mod),ir++;
// cerr<<"SB"<<" "<<dis[1][ri[ir].id]<<" "<<ri[ir].id<<endl;
}
pair<int,int> res=bit.qry(le[i].b);
ans=(ans+res.first*(dis[1][le[i].id]%mod)%mod+res.second)%mod;
// cerr<<i<<" "<<ir<<" "<<ans<<" "<<res.first<<" "<<res.second<<" "<<dis[1][le[i].id]<<" "<<le[i].id<<endl;
}
// cerr<<ans<<endl;
// cerr<<"SB2"<<endl;
fo(i,1,sl)le[i]=node{dis[1][i+(l-1)*3]-dis[2][i+(l-1)*3],dis[3][i+(l-1)*3]-dis[2][i+(l-1)*3],i+(l-1)*3};
fo(i,1,sr)ri[i]=node{dis[2][i+mid*3]-dis[1][i+mid*3],dis[2][i+mid*3]-dis[3][i+mid*3],i+mid*3};
lh=0;fo(i,1,sl)lsh[++lh]=le[i].b;fo(i,1,sr)lsh[++lh]=ri[i].b;
sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;bit.init(lh);
sort(le+1,le+sl+1);sort(ri+1,ri+sr+1);ir=1;
fo(i,1,sl)le[i].b=lower_bound(lsh+1,lsh+lh+1,le[i].b)-lsh;
fo(i,1,sr)ri[i].b=lower_bound(lsh+1,lsh+lh+1,ri[i].b)-lsh;
fo(i,1,sl){
while(ir<=sr&&ri[ir].a<le[i].a)bit.ins(ri[ir].b,dis[2][ri[ir].id]%mod),ir++;
pair<int,int> res=bit.qry(le[i].b);
ans=(ans+res.first*(dis[2][le[i].id]%mod)%mod+res.second)%mod;
}
// cerr<<ans<<endl;
// cerr<<"SB3"<<endl;
fo(i,1,sl)le[i]=node{dis[1][i+(l-1)*3]-dis[3][i+(l-1)*3],dis[2][i+(l-1)*3]-dis[3][i+(l-1)*3],i+(l-1)*3};
fo(i,1,sr)ri[i]=node{dis[3][i+mid*3]-dis[1][i+mid*3],dis[3][i+mid*3]-dis[2][i+mid*3],i+mid*3};
lh=0;fo(i,1,sl)lsh[++lh]=le[i].b;fo(i,1,sr)lsh[++lh]=ri[i].b;
sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;bit.init(lh);
sort(le+1,le+sl+1);sort(ri+1,ri+sr+1);ir=1;
fo(i,1,sl)le[i].b=lower_bound(lsh+1,lsh+lh+1,le[i].b)-lsh;
fo(i,1,sr)ri[i].b=lower_bound(lsh+1,lsh+lh+1,ri[i].b)-lsh;
fo(i,1,sl){
while(ir<=sr&&ri[ir].a<le[i].a)bit.ins(ri[ir].b,dis[3][ri[ir].id]%mod),ir++;
pair<int,int> res=bit.qry(le[i].b-1);
ans=(ans+res.first*(dis[3][le[i].id]%mod)%mod+res.second)%mod;
}
// cerr<<l<<" "<<r<<" "<<ans<<endl;
}
signed main(){
freopen("mouse.in","r",stdin);
freopen("mouse.out","w",stdout);
n=read();
fo(i,1,3)fo(j,1,n)a[i][j]=read();
fo(i,1,3)fo(j,1,n)id[i][j]=(j-1)*3+i,v[id[i][j]]=a[i][j];
int mn=inf;
fo(i,1,n){
mn=min(mn,a[2][i]);
mn+=a[1][i]+a[3][i];
ll[i]=mn;
}
mn=inf;
fu(i,n,1){
mn=min(mn,a[2][i]);
mn+=a[1][i]+a[3][i];
rr[i]=mn;
}
// fo(i,1,n)ans=(ans+res[i])%mod,cerr<<res[i]<<endl;
sol(1,n);ans=ans*2%mod;
printf("%lld\n",ans);
}
T3 束
这个题是非常棒的一个题,我对优化建图这种东西一直是不太熟悉的,但是似乎现在找到了感觉
首先我们可以将整张图拆成许多横着和竖着的线段,然后对这些线段建图,发现是\(n^2\)级别的,我们用线段树优化,然后跑0/1bfs,这个东西就是用双端队列,0往前,1往后,然后就完了
那么我就要对每个点统计横竖线段的最小值,因为横竖线段的差值一定是1,这样我们就统计差值是1的个数,最后减掉然后除以2就好啦
没写代码,懒了
QQ:2953174821