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
能打表。

浙公网安备 33010602011771号