CSP 模拟 2

T1 活动投票(P2397 yyy loves Maths VI (mode) )

摩尔投票。但是也能自己想。
因为答案出现次数超过一半,所以可以让不同数相消,最后剩下的一定是答案。

点击查看代码
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=3e5+10,len=3e4;
int a[N],n;
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();
    int cnt=0,ans=0;
	for(int i=1;i<=n;++i){
        int x=read();
        if(!cnt){ans=x;cnt++;}
        if(x!=ans){cnt--;}else cnt++;
    }
    std::cout<<ans<<'\n';
}

T2 序列(不无聊的序列 Non-boring sequences)

对于这种题,我们可以先固定一个端点,然后查询左端点的信息。
固定右端点为 \(r\),设 \(f_i\) 表示从 \(i\) 到当前的 \(r\) 出现了多少个独一无二的数,记一下每个数上一次和上上次的出现位置 \(last,llast\),遇到每个数就将 \([last+1,r]\) 区间加 \(1\),将 \([llast+1,last]\),维护一下全局最小值就行,如果最小值为 \(0\),则有不合法的,直接 boring 即可。
还有启发式分裂和扫描线做法,我不会,摆了。

点击查看代码
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e5+10;
int n,a[N],f[N];
struct TREE{
	int min,tag;
}t[N<<2];
std::unordered_map<int,int> last,llast;
inline void update(int p){t[p].min=std::min(t[p<<1].min,t[p<<1|1].min);}
inline void pushdown(int p){
	if(t[p].tag){
		t[p<<1].tag+=t[p].tag;
		t[p<<1].min+=t[p].tag;
		t[p<<1|1].tag+=t[p].tag;
		t[p<<1|1].min+=t[p].tag;
		t[p].tag=0;
	}
}
inline void change(int p,int l,int r,int x,int y,int v){
	if(x>y)return;
	if(l>=x&&r<=y){t[p].tag+=v;t[p].min+=v;return;}
	pushdown(p);
	int mid=l+r>>1;
	if(x<=mid){change(p<<1,l,mid,x,y,v);}
	if(y>mid)change(p<<1|1,mid+1,r,x,y,v);
	update(p);
}
inline int query(int p,int l,int r,int x,int y){
	if(l>=x&&r<=y){return t[p].min;}
	pushdown(p);
	int mid=l+r>>1,res=10086;
	if(x<=mid)res=std::min(res,query(p<<1,l,mid,x,y));
	if(y>mid)res=std::min(res,query(p<<1|1,mid+1,r,x,y));
	return res;
}
inline void clear(int p,int l,int r){
	t[p]={0,0};
	if(l==r)return;
	int mid=l+r>>1;
	clear(p<<1,l,mid);clear(p<<1|1,mid+1,r);
}
inline void work(){
	n=read();
	bool pd=0;
	for(int i=1;i<=n;++i){
		int x=read();
		if(pd)continue;
		change(1,1,n,last[x]+1,i,1);
		change(1,1,n,llast[x]+1,last[x],-1);
		llast[x]=last[x];
		last[x]=i;
		if(query(1,1,n,1,i)<=0)pd=1;
	}
	last.clear(),llast.clear();clear(1,1,n);
	if(!pd)std::cout<<"non-boring\n";else std::cout<<"boring\n";
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	int T=read();
	while(T--){
		work();
	}
}

T3 Legacy

原题 CF786B Legacy
线段树优化建图板子,赛时忘了不可达挂大分。可以原题题解区看 tzc_wk 的讲解,讲得很好。

点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e6;
int n,Q,s,cnt,st[N],en[N],rt1,rt2,dis[N],tot,head[N];
bool vis[N];
struct TREE{
	int ls,rs,l,r;
}t[N];
struct EDGE{
	int v,next,w;
}e[N];
inline void add(int u,int v,int w){
	e[++tot]={v,head[u],w};head[u]=tot;
}
inline void build(int p,int l,int r,int flag){
	t[p].l=l,t[p].r=r;
	if(l==r){
		if(flag){
			en[l]=p;
			add(en[l],st[l],0);add(st[l],en[l],0);
		}
		else st[l]=p;
		return;
	}
	int mid=l+r>>1;
	build(t[p].ls=++cnt,l,mid,flag);build(t[p].rs=++cnt,mid+1,r,flag);
	if(flag){
		add(p,t[p].ls,0);add(p,t[p].rs,0);
	}else{
		add(t[p].ls,p,0);add(t[p].rs,p,0);
	}
}
inline void add1(int u,int v,int w){
	add(st[u],en[v],w);
}
inline void add2(int u,int p,int l,int r,int w){
	if(t[p].l>=l&&t[p].r<=r){add(st[u],p,w);return ;}
	int mid=t[p].l+t[p].r>>1;
	if(l<=mid)add2(u,t[p].ls,l,r,w);
	if(r>mid)add2(u,t[p].rs,l,r,w);
}
inline void add3(int u,int p,int l,int r,int w){
	if(t[p].l>=l&&t[p].r<=r){add(p,en[u],w);return ;}
	int mid=t[p].l+t[p].r>>1;
	if(l<=mid)add3(u,t[p].ls,l,r,w);
	if(r>mid)add3(u,t[p].rs,l,r,w);
}
struct node{
    int dis,pos;
    bool operator <(const node &x)const{
        return x.dis<dis;
    }
};
std::priority_queue<node> q;
inline void dijkstra(){
    dis[s]=0;
    q.push((node){0,s});
    while(!q.empty()){
        int x=q.top().pos,d=q.top().dis;
        q.pop();
        if(vis[x])continue;
        vis[x]=1;
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].v;
            if(dis[y]>dis[x]+e[i].w){
                dis[y]=dis[x]+e[i].w;q.push((node){dis[y],y});
            }
        }
    }
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read(),Q=read(),s=read();
	build(++cnt,1,n,0);rt1=1;rt2=cnt+1;
	build(++cnt,1,n,1);
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=Q;++i){
		int opt=read();
		if(opt==1){
			int v=read(),u=read(),w=read();add1(v,u,w);
		}if(opt==2){
			int v=read(),l=read(),r=read(),w=read();
			add2(v,rt2,l,r,w);
		}if(opt==3){
			int v=read(),l=read(),r=read(),w=read();add3(v,rt1,l,r,w);
		}
	}
	s=st[s];
	dijkstra();
	for(int i=1;i<=n;++i){std::cout<<(dis[en[i]]>=2e14?-1:dis[en[i]])<<' ';}
}

T4 DP搬运工1

没见过的预设型 DP,抽时间写下 2,3 的题解。
考虑将这些数分着散开或者挨着散开。
\(f_{i,j,k}\) 表示只使用前 \(i\) 个数字,有 \(j\) 个间隔,当前 \(max\) 的和为 \(k\) 的方案数。
然后就没了,直接大力分讨。

  • \(i+1\) 填到左右两边,留空 \(f_{i+1,j+1,k}=2\cdot f_{i,j,k}\) 不留空 \(f_{i+1,j,k+i+1}=2\cdot f_{i,j,k}\)
  • \(i+1\) 填到中间,不留空,有靠左和靠右两种情况,有 \(j\) 个空可填,\(f_{i+1,j,k+i+1}=2\cdot j\cdot f_{i,j,k}\)
  • 填到中间,紧挨两边,把空占完了,有 \(f_{i+1,j-1,k+i+1+i+1}=j\cdot f_{i,j,k}\)
  • 填到中间,两边都留空,\(f_{i+1,j+1,k}=j\cdot f_{i,j,k}\)
    根据上面的情况之间转移就行,\(ans=f_{n,0,k}\)
    因为状态只是和空的数量有关,所以不需要考虑空的长度以及能不能填,状态转移的过程中已经考虑了所有的情况。
点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=55,mod=998244353;
inline int mo(int x){return x>=mod?x%mod:x;}
int a[N],n,tot,f[N][N][N*N],K;
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read(),K=read();
	f[1][0][0]=1;
	for(int i=1;i<n;++i){
		for(int j=0;j<i;++j){
			for(int k=0;k<=std::min(K,i*i);++k){
				f[i+1][j][k+i+1]=mo(f[i+1][j][k+i+1]+f[i][j][k]*2);//放两边没空
				f[i+1][j+1][k]=mo(f[i+1][j+1][k]+f[i][j][k]*2);//放两边有空
				f[i+1][j-1][k+i*2+2]=mo(f[i+1][j-1][k+i*2+2]+f[i][j][k]*j);//放中间没空
				f[i+1][j][k+i+1]=mo(f[i+1][j][k+i+1]+f[i][j][k]*j*2);//放
				f[i+1][j+1][k]=mo(f[i+1][j+1][k]+f[i][j][k]*j);
			}
		}
	}
	int ans=0;
	for(int i=0;i<=K;++i)ans=mo(ans+f[n][0][i]);
	std::cout<<ans%mod<<'\n';
}
点击查看

T1

值域分块,答案所在块的数量一定大于一半,其他块一定小于一半,这时知道答案的位置。
对于每个块
不会,不做

T2

对于每一个元素记一下它的控制区间,如果有一个区间不能由这些区间有交覆盖,那么非法。

T3

我超,原!线段树优化建图板子。

T4

能打表。

posted @ 2024-07-25 06:32  Ishar-zdl  阅读(59)  评论(1)    收藏  举报