CSP 模拟 5
T1 简单的序列(sequence)
原题 CF1675B Make It Increasing
不考虑负数,直接做。
点击查看代码
#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=35;
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);
int T=read();
a[0]=-2e10;
while(T--){
n=read();
bool pd=0;
for(int i=1;i<=n;++i){
a[i]=read();
if(a[i-1]>=0&&a[i]<=0){pd=1;}
}
int ans=0;
if(!pd){
int pos=0;
for(int i=n;i;--i){if(a[i]<0){pos=i;break;}}
for(int i=n-1;i>pos&&!pd;--i){
while(a[i]>=a[i+1]){
if(!a[i]){pd=1;break;}
ans++;
a[i]/=2;
}
}
for(int i=2;i<=pos&&!pd;++i){
while(a[i]<=a[i-1]){
if(a[i]==-1){pd=1;break;}
ans++;
a[i]=std::floor(a[i]*1.0/2);
}
}
}
std::cout<<(pd?-1:ans)<<'\n';
}
}
T2 简单的字符串(string)
原题 [AGC016A] Shrinking
枚举答案的字母,然后每次找后面离他最近的答案字母就行。赛时读不懂题直接没做。
点击查看代码
#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=1e4+10;
char s[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);
std::cin>>s+1;
int n=strlen(s+1);
int res=1e9;;
for(int k=0;k<=25;++k){
int ans=0;
int last=0;
for(int j=n;j;--j){
if(s[j]-'a'==k){
last=j;continue;
}
if(!last){ans++;}
else {ans=std::max(ans,last-j);}
}
res=std::min(res,ans);
}
std::cout<<res<<'\n';
}
T3 简单的博弈(tree)
原题 [AGC017D] Game on Tree
SG 函数板子,我不会,
gtm1514:
sg 函数基础应用。不会 sg 的函数的自学。
设 \(x\) 子树内博弈的 sg 函数是 \(f_x\)。考虑计算。
首先叶子的 sg 值显然是 \(0\)。对于每个儿子 \(v\),考虑他有什么后继状态。
单独考虑 \(v\) 子树内的博弈,sg 值是 \(f_v\)。但是由于 \(u,v\) 见有边,我们随时可以割掉这一条边,即 \(v\) 子树游戏中的每个状态都可以直接通向 \(sg=0\) 的后继状态。那么每个点的 sg 值都要加 \(1\),也就是 \(v\) 子树的 sg 值是 \(f_v+1\)。所有异或起来就是 \(f_u\)。后略。
部分分在提示 nim 游戏。菊花是白送的。
点击查看代码
#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,MN=23;
int n,deg[N],size[N],f[1<<MN],fa[N];
std::vector<int> e[N];
inline void dfs(int u,int fa){
f[u]=0;
for(int v:e[u])if(v!=fa)dfs(v,u),f[u]^=f[v];
if(u!=1)f[u]++;
}
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();
f[1]=0;
for(int i=2;i<=n;++i){
int u=read(),v=read();
e[u].push_back(v);e[v].push_back(u);
}
dfs(1,0);
std::cout<<(f[1]?"Alice":"Bob")<<'\n';
}
T4 困难的图论(graph)
原题 【UER #9】知识网络
首先暴力是好想的,建完虚点直接最短路即可。
点到虚点边权为 \(0\),虚点到点边权为 \(1\),枚举每个虚点为起点跑最短路,然后跑出来的距离与实际最多只会相差 \(1\),差在了是否经过虚点,根据跑出的最短路建出最短路 DAG,当一个虚点内的点是这个点的前驱时,它没有经过虚点,知道这个之后就好做了。
但是 DAG 上统计前驱一直是一个经典不可做问题,然后这题是暴力上 bitset,然后不能用 stl,要手写,并且卡空间,所以每 \(64\) 位再分个块跑多次 topo 来统计。
点击查看代码
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define int 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=510005;
int n,m,k,so[N],head[N],tot,dis[N],ans[N];
bool vis[N];
std::vector<int> p[N];
struct EDGE{int v,next,w;}e[N<<2];
struct DAG{
int in[N],f[N],de[N];
std::vector<int> e[N];
inline void add(int u,int v){e[u].push_back(v);}
inline void clear(){for(int i=1;i<=n+k;++i)e[i].clear(),in[i]=0;}
inline void topo(){
std::queue<int> q;
for(int i=1;i<=n+k;++i){
if(!in[i])q.push(i);
de[i]=in[i];
}
while(!q.empty()){
int u=q.front();q.pop();
for(int v:e[u]){
f[v]|=f[u];
de[v]--;
if(!de[v])q.push(v);
}
}
}
}G;
inline void add(int u,int v,int w){e[++tot]={v,head[u],w};head[u]=tot;}
inline void work(int s){
for(int i=1;i<=n+k;++i)vis[i]=0,dis[i]=INT_MAX/2;
std::deque<int> q;
for(int j:p[s])q.push_front(j),dis[j]=0;
while(!q.empty()){
int u=q.front();q.pop_front();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].next){
int w=e[i].w,v=e[i].v;
if(vis[v])continue;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(w==1)q.push_back(v);
else q.push_front(v);
}
}
}
G.clear();
for(int u=1;u<=n+k;++u)
for(int i=head[u];i;i=e[i].next)
if(dis[e[i].v]==dis[u]+e[i].w)
G.add(u,e[i].v),G.in[e[i].v]++;
int m=p[s].size();
for(int l=0,r;l<m;l=r+1){
r=std::min(l+63,m-1);
for(int j=1;j<=n+k;++j)G.f[j]=0;
for(int j=l;j<=r;++j)G.f[p[s][j]]|=1ull<<(j-l);
G.topo();
for(int j=1;j<=n;++j){
int d=dis[j];
if(d<INT_MAX/2){
ans[d]+=__builtin_popcountll(G.f[j]);
ans[d+1]+=(r-l+1)-__builtin_popcountll(G.f[j]);
}else{
ans[2*k]+=(r-l+1);
}
}
}
}
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(),m=read(),k=read();
for(int i=1;i<=n;++i){
so[i]=read()+n;add(i,so[i],0);add(so[i],i,1);p[so[i]].push_back(i);
}for(int i=1;i<=m;++i){
int u=read(),v=read();add(u,v,1);add(v,u,1);
}for(int i=1;i<=k;++i){
work(i+n);
}
std::cout<<0<<' ';
for(int i=1;i<=2*k;++i)std::cout<<ans[i]/2<<' ';std::cout<<'\n';
}
点击查看
T1
小细节题。
注意正负
T2
没看懂题目
T3
只会 20 状压和特殊性质
T4
建完虚点后不再考虑同类。
路径长度很短,所以可以直接往外搜 \(k\) 层,这样是 \(n^2\) 的。记忆化?记一下 \(f_{i,k}\) 表示从 \(i\) 向外搜 \(k\) 层的节点数。
对于一个 \(i\) 能够正确处理出 \(f_{i,k}\),

浙公网安备 33010602011771号