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

Page Views Count

6.11 冲刺国赛模拟 16#

T3 多边形#

原题:CodeForces-1290F Making Shapes *3500

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

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

xi>0cixi=xi<0cixim

y 类似。

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

(赛时的想法是找互质的然后按照相似来求,和正解偏差很大,数位 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) 连通的概率,求和就是答案的期望,在树上这个概率就是 1dist(u,v)

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

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

n 次不同位置出发,复杂度 O(n3)

点击查看代码
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 坐标上有交的线段,将左端点的较大值作为参考,比较线段上下关系,这样每次找枚举左端点上下连边即可。

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

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

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

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

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

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

点击查看代码
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 省选模拟(沈子舜)

作者:SoyTony

出处:https://www.cnblogs.com/SoyTony/p/Simulation_Problems_of_NOI_in_Xian_June_1.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   SoyTony  阅读(106)  评论(11编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示