Loading

【考后总结】6 月西安 NOI 模拟赛 1

Page Views Count

6.11 冲刺国赛模拟 16

T3 多边形

原题:CodeForces-1290F Making Shapes *3500

凸多边形说明合法方案中同一种向量必须连续且多种顺序只算一个,因此直接计算各个向量选择的个数。

设第 \(i\) 个向量选了 \(c_i\) 个,按照两个方向的正负分,可以写作:

\[\sum_{x_i>0} c_ix_i=-\sum_{x_i<0} c_ix_i\le m \]

\(y\) 类似。

于是相当于构造出的方案要满足四个数都 \(\le m\),可以数位 DP,按位枚举 \(c\),在每一位上枚举的复杂度是 \(O(2^n)\),注意到这里是有 \(|x|,|y|\le \omega=4\) 的,因此可能有进位,只能从低位到高位,设 \(f(i,px,py,nx,ny,limx,limy)\) 表示枚举到 \(2^i\) 位,当前在 \(2^i\) 位的值分别为 \(px,py,nx,ny\),当前 \(x,y\) 是否卡上界(\(\le m\) 的条件)状态数是 \(O((n\omega)^42^n\log m)\),这也是复杂度。

(赛时的想法是找互质的然后按照相似来求,和正解偏差很大,数位 DP 可以这样使用也蛮高级的)

点击查看代码
int n,m;
int x[maxn],y[maxn];
int f[maxlog][maxw][maxw][maxw][maxw][2][2];
bool vis[maxlog][maxw][maxw][maxw][maxw][2][2];
int dfs(int now,int x1,int x2,int y1,int y2,bool limx,bool limy){
	if(now==30) return !x1&&!x2&&!y1&&!y2&&limx&&limy;
	if(vis[now][x1][x2][y1][y2][limx][limy]) return f[now][x1][x2][y1][y2][limx][limy];
	vis[now][x1][x2][y1][y2][limx][limy]=true;
	int res=0;
	for(int s=0;s<(1<<n);++s){
		int tmpx1=x1,tmpx2=x2,tmpy1=y1,tmpy2=y2;
		for(int i=1;i<=n;++i){
			if(s&(1<<i-1)){
				if(x[i]>0) tmpx1+=x[i];
				else tmpx2-=x[i];
				if(y[i]>0) tmpy1+=y[i];
				else tmpy2-=y[i];
			}
		}
		if((tmpx1^tmpx2)&1||(tmpy1^tmpy2)&1) continue;
		bool chkm=(m>>now)&1,chkx=tmpx1&1,chky=tmpy1&1;
		res=(res+dfs(now+1,tmpx1/2,tmpx2/2,tmpy1/2,tmpy2/2,chkm?(!chkx||limx):(!chkx&&limx),chkm?(!chky||limy):(!chky&&limy)))%mod;
	}
	return f[now][x1][x2][y1][y2][limx][limy]=res;
}	
int ans;
int main(){
	freopen("polygon.in","r",stdin);
	freopen("polygon.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;++i){
		x[i]=read(),y[i]=read();
	}
	ans=(dfs(0,0,0,0,0,true,true)-1+mod)%mod;
	printf("%d\n",ans);
	return 0;
}

6.12 冲刺国赛模拟 17

T1 掌分治

树上操作很经典,考虑枚举点对 \((u,v)\) 计算删除 \(u\)\((u,v)\) 连通的概率,求和就是答案的期望,在树上这个概率就是 \(\dfrac{1}{\mathrm{dist}(u,v)}\)

放在一个环上,设 \(u\to v\) 两条路径的点数分别是 \(a,b\),那么只需要保证其中一条没被删除就行了,同时都没被删除需要容斥一下,答案就是 \(\dfrac{1}{a}+\dfrac{1}{b}-\dfrac{1}{a+b-1}\),这里考场上想的是枚举这些点中 \(u\) 删的时刻再累加,就不太好拓展了。

放在仙人掌上,先建出圆方树,考虑多个环和链结合起来的答案容斥部分并非简单的相乘,考虑固定前面走法不变,长度为 \(l\),增加一个环,此时产生影响是 \(\dfrac{1}{l+a}+\dfrac{1}{l+b}-\dfrac{1}{l+a+b-1}\),因此增加一个环后这些分母增加,而分母一定在 \([1,n]\) 内,考虑 DP 系数,转移类似背包。由于合并多个环可能相交部分点被算两次,所以钦定当前的环长度不算方点父亲,最终平移一位就行。

\(n\) 次不同位置出发,复杂度 \(O(n^3)\)

点击查看代码
int n,m;
int inv[maxn];
struct edge{
    int to,nxt;
}e[maxn<<2];
int head[maxn],cnt;
inline void add_edge(int u,int v){
    e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;
    e[++cnt].to=u,e[cnt].nxt=head[v],head[v]=cnt;
}
int dfn[maxn],low[maxn],dfncnt;
int st[maxn],top;
int tot;
vector<int> E[maxn<<1];
unordered_map<int,int> mp[maxn];
void Tarjan(int u){
    dfn[u]=++dfncnt,low[u]=dfn[u];
    st[++top]=u;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(!dfn[v]){
            Tarjan(v);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]){
                ++tot;
                int id=0;
                while(st[top]!=v){
                    E[st[top]].push_back(tot),E[tot].push_back(st[top]);
                    mp[tot-n][st[top]]=++id;
                    --top;
                }
                E[v].push_back(tot),E[tot].push_back(v);
                mp[tot-n][v]=++id;
                --top;
                E[u].push_back(tot),E[tot].push_back(u);
                mp[tot-n][u]=++id;
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}
int f[maxn][maxn];
void dfs(int u,int fa){
    if(u<=n){
        f[u][0]=1;
        for(int v:E[u]){
            if(v==fa) continue;
            dfs(v,u);
        }
    }
    else{
        if((int)E[u].size()==2){
            for(int v:E[u]){
                if(v==fa) continue;
                dfs(v,u);
                for(int i=0;i+1<n;++i) f[fa][i+1]=(f[fa][i+1]+f[v][i])%mod;
            }
        }
        else{
            int id=mp[u-n][fa];
            int now=0;
            for(int v:E[u]){
                ++now;
                if(v==fa) continue;
                dfs(v,u);
                int k1=abs(now-id),k2=(int)E[u].size()-k1;
                for(int i=0;i+k1<n;++i) f[fa][i+k1]=(f[fa][i+k1]+f[v][i])%mod;
                for(int i=0;i+k2<n;++i) f[fa][i+k2]=(f[fa][i+k2]+f[v][i])%mod;
                for(int i=0;i+k1+k2-1<n;++i) f[fa][i+k1+k2-1]=(f[fa][i+k1+k2-1]-f[v][i]+mod)%mod;
            }
        }
    }
}
int ans;
int main(){
    freopen("cactus.in","r",stdin);
    freopen("cactus.out","w",stdout);
    n=read(),m=read();
    inv[1]=1;
    for(int i=2;i<=n;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=m;++i){
        int u=read(),v=read();
        add_edge(u,v);
    }
    tot=n;
    Tarjan(1);
    for(int i=1;i<=n;++i){
        memset(f,0,sizeof(f));
        dfs(i,0);
        for(int j=0;j<n;++j) ans=(ans+1ll*f[i][j]*inv[j+1]%mod+mod)%mod;
    }
    printf("%d\n",ans);
    return 0;
}

T3 下大雨

原题:洛谷-P6256 ICPC 2019 WF Directing Rainfall

由于存在一些遮挡关系,雨从一个位置下落是有先后顺序的,满足拓扑关系,拓扑的条件类似扫描线,从左到右扫描,对于两个 \(x\) 坐标上有交的线段,将左端点的较大值作为参考,比较线段上下关系,这样每次找枚举左端点上下连边即可。

这样找到一个拓扑关系,我们可以模拟这个下落的过程,设 \(f_i\) 表示雨从空中落下最终落在 \((i,0)\) 的最小操作次数,从上到下枚举线段,对于一个 \(x\) 坐标线段 \([l,r]\),如果左低右高,那么首先从 \(i\) 位置落下的雨可以选择落到 \([l,i]\),因此是 \(f\) 值在 \([l,r]\) 取后缀最小值,而在 \([l+1,r]\) 位置直接落下需要操作一次,因此再区间加 \(1\),这个操作并不好执行。

考虑到操作都是区间,设 \(g_i=f_i-f_{i-1}\),那么第二个操作就是单点了,而第一个操作实际上就是在 \(i-1\)\(i\) 都在 \([l,r]\) 内的 \(g\) 会发生改变,以后缀最小值为例,所有 \(g_i<0\) 的位置都应该变为 \(0\),但同时也会对后面造成影响。

使用 set 维护 \(g_i\neq 0\) 的位置,如果 \(g_i<0\) 则向前找 \(g_j>0\) 的位置与 \(g_i\) 抵消,直到 \(g_i=0\) 或超过了取最小值的范围,即 \(g_{l}\)。如果此时 \(g_i\) 还有剩余,这个时候因为 \(g_l\) 及更前不能取最小值,可以把剩下的数全减在 \(g_l\) 位置。注意这个只招 \(g_j>0\) 的位置实际上是简化了差分数组依次向左改变,略去了 \(g_j=0\) 减小又增大本质不变的过程。

同时考虑到前后缀最小值都要维护,分别维护 \(g\) 正负两个部分是比较可行的。注意取最小值和加法两个操作不能颠倒。

另外雨滴可以从非整数的位置落下且不经过任何遮挡,所以横坐标扩大一倍即可。

复杂度考虑势能分析,每次操作使 \(g\) 的绝对值增加 \(2\),而每次取 \(\min\) 时将两个 set 维护的信息抵消,相当于绝对值减小且至少减小 \(2\),因此操作次数是 \(O(n)\),算上查询前驱后继的复杂度是 \(O(n\log n)\)

点击查看代码
int n,L,R;
struct Segment{
    int id,x1,y1,x2,y2;
    Segment()=default;
    Segment(int id_,int x1_,int y1_,int x2_,int y2_):id(id_),x1(x1_),y1(y1_),x2(x2_),y2(y2_){}
    bool operator<(const Segment& rhs)const{
        if(x1<rhs.x1) return 1ll*(y2-y1)*(rhs.x1-x1)+1ll*y1*(x2-x1)<1ll*rhs.y1*(x2-x1);
        else return 1ll*y1*(rhs.x2-rhs.x1)<1ll*(rhs.y2-rhs.y1)*(x1-rhs.x1)+1ll*rhs.y1*(rhs.x2-rhs.x1);
    }
}Seg[maxn];
struct Data{
    int id,val;
    Data()=default;
    Data(int id_,int val_):id(id_),val(val_){}
    bool operator<(const Data& rhs)const{
        return val>rhs.val;
    }
};
set<Segment> S;
priority_queue<Data> Q;
vector<int> E[maxn];
int deg[maxn];
queue<int> q;
multiset<pii> Sp,Sn;
int main(){
    freopen("rain.in","r",stdin);
    freopen("rain.out","w",stdout);
    L=read()*2,R=read()*2,n=read();
    for(int i=1;i<=n;++i){
        int x1=read()*2,y1=read(),x2=read()*2,y2=read();
        if(x1>x2) swap(x1,x2),swap(y1,y2);
        Seg[i]=Segment(i,x1,y1,x2,y2);
    }
    sort(Seg+1,Seg+n+1,[&](Segment A,Segment B){
        return A.x1<B.x1;
    });
    for(int i=1;i<=n;++i) Seg[i].id=i;
    for(int i=1;i<=n;++i){
        while(!Q.empty()&&Q.top().val<Seg[i].x1){
            S.erase(Seg[Q.top().id]);
            Q.pop();
        }
        auto it=S.lower_bound(Seg[i]);
        if(it!=S.end()){
            E[(*it).id].push_back(i);
            ++deg[i];
        }
        if(it!=S.begin()){
            --it;
            E[i].push_back((*it).id);
            ++deg[(*it).id];
        }
        S.insert(Seg[i]);
        Q.push(Data(i,Seg[i].x2));
    }
    for(int i=1;i<=n;++i){
        if(!deg[i]) q.push(i);
    }
    Sp.insert(mp(R+1,inf));
    Sn.insert(mp(L,inf));
    while(!q.empty()){
        int u=q.front();
        q.pop();
        if(Seg[u].y1<Seg[u].y2){
            auto it1=Sn.lower_bound(mp(Seg[u].x2+1,-inf));
            if(it1!=Sn.begin()){
                --it1;
                while((*it1).fir>=Seg[u].x1+1){
                    int val=(*it1).sec;
                    auto it2=Sp.lower_bound(mp((*it1).fir+1,-inf));
                    if(it2!=Sp.begin()){
                        --it2;
                        while(val&&(*it2).fir>=Seg[u].x1){
                            if((*it2).sec>val){
                                Sp.insert(mp((*it2).fir,(*it2).sec-val));
                                Sp.erase(it2);
                                val=0;
                                break;
                            }
                            else{
                                val-=(*it2).sec;
                                auto tmp=it2;
                                if(it2!=Sp.begin()){
                                    --it2;
                                    Sp.erase(tmp);
                                }
                                else{
                                    Sp.erase(tmp);
                                    break;
                                }
                            }
                        }
                    }   
                    if(val) Sn.insert(mp(Seg[u].x1,val));
                    auto tmp=it1;
                    if(it1!=Sn.begin()){
                        --it1;
                        Sn.erase(tmp);
                    }
                    else{
                        Sn.erase(tmp);
                        break;
                    }
                }
            }
            Sp.insert(mp(Seg[u].x1+1,1));
            Sn.insert(mp(Seg[u].x2+1,1));
        }
        else{
            auto it1=Sp.lower_bound(mp(Seg[u].x1,-inf));
            while(it1!=Sp.end()&&(*it1).fir<=Seg[u].x2){
                int val=(*it1).sec;
                auto it2=Sn.lower_bound(mp((*it1).fir,-inf));
                while(it2!=Sn.end()&&val&&(*it2).fir<=Seg[u].x2+1){
                    if((*it2).sec>val){
                        Sn.insert(mp((*it2).fir,(*it2).sec-val));
                        Sn.erase(*it2);
                        val=0;
                        break;
                    }
                    else{
                        val-=(*it2).sec;
                        auto tmp=it2;
                        ++it2;
                        Sn.erase(tmp);
                        if(it2==Sn.end()) break;
                    }
                }
                if(val) Sp.insert(mp(Seg[u].x2+1,val));
                auto tmp=it1;
                ++it1;
                Sp.erase(tmp);
            }
            Sp.insert(mp(Seg[u].x1,1));
            Sn.insert(mp(Seg[u].x2,1));
        }
        for(int v:E[u]){
            --deg[v];
            if(!deg[v]) q.push(v);
        }
    }
    for(auto it=Sn.begin();it!=Sn.end();++it){
        Sp.insert(mp((*it).fir,-(*it).sec));
    }
    int ans=inf,sum=inf,now=-inf;
    Sp.insert(mp(L,0));
    for(auto it=Sp.begin();it!=Sp.end();++it){
        if((*it).fir!=now&&now>=L&&now<=R) ans=min(ans,sum);
        now=(*it).fir,sum+=(*it).sec;
    }
    printf("%d\n",ans);
    return 0;
}

6.13 冲刺国赛模拟 18

3.29 省选模拟(沈子舜)

posted @ 2023-06-13 10:05  SoyTony  阅读(87)  评论(11编辑  收藏  举报