• 【学习笔记】NOIP模拟赛25


    据说是llsw出的题

    我是没上200的丝薄

    A. 遗忘十字路

    f u , K f_{u,K} fu,K表示从 u u u出发,走 K K K次后价值和的最大值。

    假设有 m m m个儿子,那么 f u , K = ∑ v f v , K / m + ∑ v ′ f v ′ , K / m + 1 f_{u,K}=\sum_v f_{v,K/m}+\sum_{v'}f_{v',K/m+1} fu,K=vfv,K/m+vfv,K/m+1

    直接排序即可。应该可以感觉到状态数是线性的。

    复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

    #include
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    using namespace std;
    int T,n;
    ll a[500005],K;
    vector<int>g[500005];
    vector<ll>vec[500005];
    vector<pair<int,ll>>id[500005];
    inline int read() {
    	int x=0,f=1;
    	char c=getchar();
    	while(c<'0'||c>'9') {
    		if(c=='-') f=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9') {
    		x=(x<<1)+(x<<3)+c-'0';
    		c=getchar(); 
    	}
    	return x*f;
    }
    ll dfs(int u,int topf,ll K){
    	int m=g[u].size()-(u!=1);if(m==0)return K*a[u];
    	for(auto x:id[u])if(x.fi==K)return x.se;
    	vec[u].clear();ll tot=0;
    	for(auto v:g[u]){
    		if(v!=topf){
    			ll x=dfs(v,u,K/m),y=dfs(v,u,K/m+1);
    			tot+=x,vec[u].pb(y-x);
    		}
    	}sort(vec[u].begin(),vec[u].end()),reverse(vec[u].begin(),vec[u].end());
    	for(int j=0;j<K-K/m*m;j++)tot+=vec[u][j];
    	id[u].pb(make_pair(K,tot+K*a[u]));
        return tot+K*a[u];
    }
    int main(){
    	freopen("crossroad.in","r",stdin);
    	freopen("crossroad.out","w",stdout);
    	T=read();
    	while(T--){
    		n=read(),K=read();for(int i=1;i<=n;i++)a[i]=read(),g[i].clear(),id[i].clear();
    		for(int i=1;i<n;i++){
    			int u=read(),v=read();g[u].pb(v),g[v].pb(u);
    		}printf("%lld\n",dfs(1,0,K));
    	}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    B. 鹿角虫道

    原题为「Gym102331C」Counting Cactus

    姑且称之为仙人掌计数。

    f ( u , S ) f(u,S) f(u,S)表示 u u u号节点为根,节点集合为 S S S的方案数。

    g ( u , S ) g(u,S) g(u,S)表示 u u u号节点为根,且 u u u恰好只在一个环上,节点集合为 S S S的方案数。

    d p ( u , v , S ) dp(u,v,S) dp(u,v,S)为钦定一个当前环的开头是 u u u,环尾扩展到了 v v v,节点集合为 S S S的方案数。

    转移方程为:

    1.1 1.1 1.1 v v v为编号最小的节点,并且 v ∈ T v\in T vT ( f v , T + g u , T + { u } ) × f u , S − T → f u , S (f_{v,T}+g_{u,T+\{u\}})\times f_{u,S-T}\to f_{u,S} (fv,T+gu,T+{u})×fu,STfu,S
    1.2 1.2 1.2 u u u在环上,且环的终点为 v v v ( u , x ) ∈ E (u,x)\in E (u,x)E d p x , v , S − { u } → f u , S , g u , S dp_{x,v,S-\{u\}}\to f_{u,S},g_{u,S} dpx,v,S{u}fu,S,gu,S
    1.3 1.3 1.3 对于 u → v u\to v uv的链,在 u u u上挂一个仙人掌,满足 ( u , x ) ∈ E (u,x)\in E (u,x)E u ∈ T u\in T uT x , v ∉ T x,v\notin T x,v/T d p x , v , S − T × f u , T → d p u , v , S dp_{x,v,S-T}\times f_{u,T}\to dp_{u,v,S} dpx,v,ST×fu,Tdpu,v,S

    注意环有顺逆时针两种情况,所以要 / 2 /2 /2

    复杂度 O ( 3 n n 3 ) O(3^nn^3) O(3nn3)。上界很松。通过固定转移系数 f u , T f_{u,T} fu,T可以优化至 O ( 3 n n 2 ) O(3^nn^2) O(3nn2)

    有更好的方法,但是我不会。

    #include
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    using namespace std;
    const int mod=998244353;
    int n,m,a[13][13];
    ll inv2,f[13][1<<13],dp[13][13][1<<13],g[13][1<<13],res;
    void add(ll &x,ll y){
    	x=(x+y)%mod;
    }
    ll fpow(ll x,ll y){
    	ll z(1);
    	for(;y;y>>=1){
    		if(y&1)z=z*x%mod;
    		x=x*x%mod;
    	}return z;
    }
    int main(){
    	freopen("stag.in","r",stdin);
    	freopen("stag.out","w",stdout);
    	cin>>n>>m,inv2=fpow(2,mod-2);for(int i=1;i<=m;i++){
    		int u,v;cin>>u>>v,u--,v--,a[u][v]=a[v][u]=1;
    	}for(int i=0;i<n;i++)f[i][0]=1;
    	for(int s=1;s<1<<n;s++){
    		for(int i=0;i<n;i++){
    			if(s>>i&1) {
    				for(int j=0;j<n;j++){
    					if(i==j||!(s>>j&1))continue;
    					for(int k=0;k<n;k++){
    						if(i==j||j==k||!(s>>k&1)||!a[i][k])continue;
    						for(int s2=s-(1<<j)-(1<<k);s2;s2=(s2-1)&(s-(1<<j)-(1<<k))){
    							if(s2>>i&1)add(dp[i][j][s],dp[k][j][s-s2]*f[i][s2-(1<<i)]);
    						}
    					}
    					if(a[i][j]){
    						for(int s2=s-(1<<j);s2;s2=(s2-1)&(s-(1<<j))){
    							if(s2>>i&1)add(dp[i][j][s],f[i][s2-(1<<i)]*f[j][s-s2-(1<<j)]);
    						}
    					}
    					
    				}
    			}
    		}
    		for(int i=0;i<n;i++){
    			if(!(s>>i&1)){
    				int j=0;while(!(s>>j&1))j++;
    				for(int s2=s;s2;s2=(s2-1)&s){
    					if(!(s2>>j&1))continue;
    					add(f[i][s],g[i][s2]*f[i][s-s2]);
    					for(int k=0;k<n;k++){
    						if((s2>>k&1)&&a[i][k]){
    							add(f[i][s],f[j][s2-(1<<j)]*f[i][s-s2]);
    						}
    					}
    				}
    				for(int j=0;j<n;j++){
    					if(i==j||!(s>>j&1)||!a[i][j])continue;
    					for(int k=0;k<n;k++){
    						if(i==k||j==k||!(s>>k&1)||!a[i][k])continue;
    						add(f[i][s],dp[k][j][s]*inv2);
    						add(g[i][s],dp[k][j][s]*inv2);
    					}
    				}
    			}
    		}
    	}
    	cout<<f[0][(1<<n)-2];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    C. 泪水之城

    神秘多项式。。。

    D. 生命血

    原题为 「Gym102268D」Dates

    应该可以看出来这是一个拟阵。

    不妨将其转化为二分图匹配,使用 hall \text{hall} hall定理,只需关注右部点中的一段连续区间。问题转述为,对于任意 1 ≤ i ≤ j ≤ m 1\le i\le j\le m 1ijm s r j − s l i − 1 ≥ j − i + 1 s_{r_j}-s_{l_i-1}\ge j-i+1 srjsli1ji+1,其中 s i s_i si表示 a i a_i ai的前缀和。

    不妨将话说明白些。分参易得 s r j − j ≥ s l i − 1 − i + 1 s_{r_j}-j\ge s_{l_i-1}-i+1 srjjsli1i+1

    我们将其视作位置 i i i的排名 f i f_i fi, g i g_i gi。那么对于任意 j ≥ i j\ge i ji f j ≥ g i f_j\ge g_i fjgi。每次插入一个操作只会影响 i i i, j j j(相对位置),因此前面的排名不变,设插入位置为 k k k,只需判断 max ⁡ i = 1 k g i \max_{i=1}^k g_i maxi=1kgi min ⁡ i = k m f i \min_{i=k}^mf_i mini=kmfi的大小关系,因此用两颗线段树维护。

    复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

    #include
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define int ll
    #define inf 0x3f3f3f3f3f3f3f3f
    using namespace std;
    int n,m,T,L[300005],R[300005],a[300005],s[300005],bit[300005];
    struct node{
    	int dat,max,min;
    }t[300005*4];
    struct node2{
    	int v,l,r,id;
    	bool operator <(const node2 &a)const{
    		return v>a.v;
    	}
    }q[300005];
    vector<int>vec;
    void add(int x,int k){
    	for(;x<=m;x+=x&-x)bit[x]+=k;
    }
    int ask(int x){
    	int tot(0);for(;x;x-=x&-x)tot+=bit[x];return tot;
    }
    void pushup(int p){
    	t[p].max=max(t[p<<1].max,t[p<<1|1].max);
    	t[p].min=min(t[p<<1].min,t[p<<1|1].min);
    }
    void dat(int p,int x){
    	t[p].max+=x,t[p].min+=x,t[p].dat+=x;
    }
    void pushdown(int p){
    	if(t[p].dat)dat(p<<1,t[p].dat),dat(p<<1|1,t[p].dat),t[p].dat=0;
    }
    void build(int p,int l,int r){
    	t[p].dat=0;t[p].min=inf,t[p].max=-inf;
    	if(l==r)return;
    	int mid=l+r>>1;build(p<<1,l,mid),build(p<<1|1,mid+1,r);
    }
    void mdy(int p,int l,int r,int x,int y,int z){
    	if(l==r){t[p].min=y,t[p].max=z;return;}
    	pushdown(p);int mid=l+r>>1;
    	x<=mid?mdy(p<<1,l,mid,x,y,z):mdy(p<<1|1,mid+1,r,x,y,z);
    	pushup(p);
    }
    void upd(int p,int l,int r,int ql,int qr,int x){
    	if(ql>qr)return;
    	if(ql<=l&&r<=qr){dat(p,x);return;}
    	pushdown(p);int mid=l+r>>1;
    	if(ql<=mid)upd(p<<1,l,mid,ql,qr,x);if(mid<qr)upd(p<<1|1,mid+1,r,ql,qr,x);
    	pushup(p); 
    }
    int qry(int p,int l,int r,int ql,int qr,int op){
    	if(ql>qr)return op?-inf:inf;
    	if(ql<=l&&r<=qr)return op?t[p].max:t[p].min;
    	pushdown(p);int mid=l+r>>1;
    	if(qr<=mid)return qry(p<<1,l,mid,ql,qr,op);
    	if(mid<ql)return qry(p<<1|1,mid+1,r,ql,qr,op);
    	if(op==0)return min(qry(p<<1,l,mid,ql,qr,op),qry(p<<1|1,mid+1,r,ql,qr,op));
    	return max(qry(p<<1,l,mid,ql,qr,op),qry(p<<1|1,mid+1,r,ql,qr,op));
    }
    signed main(){
    	freopen("lifeblood.in","r",stdin);
    	freopen("lifeblood.out","w",stdout);
    	ios::sync_with_stdio(false);
    	cin.tie(0),cout.tie(0);
    	cin>>T;
    	while(T--){
    		cin>>n>>m;for(int i=1;i<=n;i++)cin>>a[i],s[i]=s[i-1]+a[i];
    		for(int i=1;i<=m;i++){
    			int l,r,v;cin>>l>>r>>v;
    			q[i]={v,l,r,i},L[i]=l,R[i]=r;
    		}sort(q+1,q+1+m);
    		vec.clear();ll res=0;build(1,1,m);for(int i=1;i<=m;i++)bit[i]=0;
    		for(int i=1;i<=m;i++){
    			int l=q[i].l,r=q[i].r,k=q[i].id;
    			mdy(1,1,m,k,s[r]-ask(k),s[l-1]-ask(k)+1);
    			upd(1,1,m,k+1,m,-1);
    			if(qry(1,1,m,1,k,1)>qry(1,1,m,k,m,0)){
    				mdy(1,1,m,k,inf,-inf),upd(1,1,m,k+1,m,1);
    			}
    			else {
    				vec.pb(k),res+=q[i].v,add(k,1);
    			}
    		}cout<<res<<"\n"<<vec.size()<<"\n";
    		if(vec.size()){
    			for(int i=0;i<vec.size()-1;i++){
    				cout<<vec[i]<<' ';
    			}cout<<vec.back();
    			cout<<"\n";
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
  • 相关阅读:
    MYSQLg高级-----SQL注入的理解(初级篇)以及如何防止注入
    Helm实战案例一:在Kubernetes上使用Helm搭建Prometheus Operator监控
    Vue3 从入门到放弃 (第六篇.插槽(Slot)的使用)
    Java设计模式之迭代器模式
    Apache IoTDB 0.13.0 权限控制不当漏洞
    如何使用OpenCV的随机森林(Python)
    C2025 基础进阶——模拟与枚举
    公众号爆文写作怎么做?或许这些领域才适合你!
    从行业角度看,数仓领域的未来是什么?
    【前端精进之路】JS篇:第5期 JS引擎线程的执行过程的三个阶段
  • 原文地址:https://blog.csdn.net/cqbzlydd/article/details/127805152