NOIP模拟73
Posted Varuxn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP模拟73相关的知识,希望对你有一定的参考价值。
T1 小L的疑惑
解题思路
第一眼不是正解,又是 bitset
优化可以得到的 60pts 的部分分。
打着打着突然发现这个东西好像和之前做过的某个题有一些相似,试着打了一下。
然后样例过了,然后对拍没错,然后就切了??
先排序,如果某个数之前所有数的和都比这个数字-1 小,那么这里就是一个空缺,扫的时候选择最小的空缺就是答案。
code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e5+10,M=1e7+10,INF=1e18;
int n,cnt,s[N];
signed main()
{
freopen("math.in","r",stdin); freopen("math.out","w",stdout);
n=read(); for(int i=1;i<=n;i++) s[i]=read(); sort(s+1,s+n+1);
for(int i=1;i<=n;i++)
{
if(cnt<s[i]-1) printf("%lld",cnt+1),exit(0);
cnt+=s[i];
}
printf("%lld",cnt+1);
return 0;
}
T2 小L的数列
解题思路
发现运算都是乘法,于是我们考虑转化成为指数上的加法。
然后我们又看了一下 k 的范围,大概是矩阵乘法没错了,于是很好出来下移的做法复杂度大概是 \\(k^3log(n-k)\\)
但是我考场上一时糊涂竟然没有想到,于是整到了一个 \\(k^3log\\frac{n-k}{k}\\) 的做法,也就是一次性转移 k 个。
单位矩阵的第一行就是 \\(b_k,b_{k-1},...b_2,b_1\\) 第二行其实就是 \\(0,b_{k},...b_3,b_2\\) 再加上第一行的系数每一项乘上一个 \\(b_1\\) 其它的系数也是类似。
然后我们就可以一次性转移 k 个了,免去了卡常的麻烦。
code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=4e7+10,M=210,mod=998244353;
int n,m,Ans=1,f[M],b[M];
struct Square
{
int a[M][M];
void clear(){memset(a,0,sizeof(a));}
Square friend operator * (Square x,Square y)
{
Square z; z.clear();
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=m;k++)
z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j])%(mod-1);
return z;
}
}e,ans;
void pw(Square &x,int y)
{
while(y)
{
if(y&1) x=x*e;
e=e*e; y>>=1;
}
}
int power(int x,int y,int p=mod)
{
int temp=1;
while(y)
{
if(y&1) temp=temp*x%mod;
x=x*x%mod; y>>=1;
}
return temp;
}
signed main()
{
freopen("seq.in","r",stdin); freopen("seq.out","w",stdout);
n=read(); m=read();
for(int i=1;i<=m;i++) b[i]=read();
for(int i=1;i<=m;i++) f[i]=read();
for(int i=1;i<=m;i++) ans.a[i][i]=1;
for(int i=1;i<=m;i++)
{
for(int j=i;j<=m;j++) e.a[i][j]=b[m-(j-i)];
for(int j=1;j<i;j++)
for(int k=1;k<=m;k++)
e.a[i][k]=(e.a[i][k]+e.a[j][k]*b[i-j])%(mod-1);
}
pw(ans,n/m+(n%m!=0)-1);
int pos=n%m; if(!pos) pos=m;
for(int i=1;i<=m;i++)
Ans=Ans*power(f[i],ans.a[pos][i])%mod;
printf("%lld",Ans);
return 0;
}
T3 连边
解题思路
尽管实际得分远高于期望得分,但是还是挂了 20pts 没有场上切掉(感觉不稳就判了一下暴力,然后暴力被卡常了。。)
大概就是一个多源最短路同时记录一下前驱,对于只有一个前驱的直接选择,对于多个前驱的选择权值最小的。。
本来我只是想对于随机数据下手的,想随便 rand()
一下,但是感觉不稳,就贪了个心,然后歪打正着??(算是吧。。)
code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e5+10,M=N<<1,INF=1e18;
int n,m,ans=INF,dis[N],fa[N];
int tot=1,head[N],nxt[M<<1],ver[M<<1],edge[M<<1];
bool col[N],vis[N],can[M];
struct Road{int l,r,val;}pat[M];
priority_queue<pair<int,int> > q;
vector<pair<int,int> > pre[N];
void add_edge(int x,int y,int val)
{
ver[++tot]=y; edge[tot]=val;
nxt[tot]=head[x]; head[x]=tot;
}
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
signed main()
{
freopen("minimum.in","r",stdin); freopen("minimum.out","w",stdout);
n=read(); m=read();
for(int i=1;i<=n;i++) vis[i]=col[i]=read(),fa[i]=i;
for(int i=1,x,y,val;i<=m;i++)
{
x=read(); y=read(); val=read(); pat[i]=(Road){x,y,val},
add_edge(x,y,val); add_edge(y,x,val);
if(find(x)==find(y)) continue;
vis[find(y)]|=vis[find(x)];
fa[find(x)]=find(y);
}
for(int i=1;i<=n;i++) if(!vis[find(i)]) printf("impossible"),exit(0);
memset(dis,0x3f,sizeof(dis)); memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++) if(col[i]) dis[i]=0,q.push(make_pair(0,i));
while(!q.empty())
{
int x=q.top().second; q.pop();
if(vis[x]) continue; vis[x]=true;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i],val=edge[i];
if(dis[to]<dis[x]+val) continue;
if(dis[to]>dis[x]+val) vector<pair<int,int> >().swap(pre[to]);
dis[to]=dis[x]+val; pre[to].push_back(make_pair(x,i));
if(!vis[to]) q.push(make_pair(-dis[to],to));
}
}
for(int i=1;i<=n;i++) if(pre[i].size()==1) can[pre[i][0].second>>1]=true;
for(int i=1;i<=n;i++)
if(pre[i].size()!=1)
{
int minn=INF,id=0;
for(int j=0;j<pre[i].size();j++) if(can[pre[i][j].second>>1]) goto X;
for(int j=0;j<pre[i].size();j++) if(minn>edge[pre[i][j].second]) id=pre[i][j].second,minn=edge[id],id>>=1;
can[id]=true; X:;
}
ans=0;
for(int i=1;i<=m;i++) ans+=can[i]*pat[i].val;
printf("%lld",ans);
return 0;
}
T4 小L的有向图
解题思路
看到 T4 的时候只有 30min 了,感觉不是特别好搞,我直接 printf("0")
然后去看前面的了。。
考完之后听了一下正解思路感觉神似竞赛图,但又不完全是。
\\(f_{S}\\) 表示 \\(S\\) 点集内部合法拓扑序的个数然后枚举集合外面的点,计算集合内点到对应点的连边数量作为 2 的指数就是这个点的贡献。
code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=25,mod=998244353;
int n,m,e[N],f[1<<22],sum[1<<22];
int lowbit(int x){return x&(-x);}
signed main()
{
freopen("topology.in","r",stdin); freopen("topology.out","w",stdout);
n=read(); m=read();
for(int i=1,x,y;i<=m;i++) x=read(),y=read(),e[y]|=1<<x-1;
for(int i=1;i<=n;i++) sum[1<<i-1]=1; f[0]=1;
for(int i=1;i<(1<<n);i++)sum[i]=sum[i^lowbit(i)]+sum[lowbit(i)];
for(int sta=0;sta<(1<<n);sta++)
for(int i=1;i<=n;i++)
{
if((sta>>i-1)&1) continue;
int temp=sum[sta&e[i]];
f[sta|(1<<i-1)]=(f[sta|(1<<i-1)]+f[sta]*(1ll<<temp))%mod;
}
printf("%lld",f[(1<<n)-1]);
return 0;
}
T? 中国象棋
解题思路
由于今天的考试题目比较简单,教练就又加了几道题,看了看好像就这道比较好做,大致口胡一下。
显然每行每列最多只可能有两个棋子,于是设 \\(f_{i,j,k}\\) 表示前 i 行,有 j 列只有一个棋子,有 k 列只有两个棋子。
然后分别枚举不放旗子,在有一个棋子的列放一个,在没有的放一个或者两个,在没有的有一个的各放一个,在有一个的放两列。
code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=110,mod=9999973;
int n,m,ans,f[N][N][N];
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int C2(int x){return x*(x-1)/2;}
void solve(int i,int j,int k)
{
if(!f[i][j][k]) return ;
add(f[i+1][j][k],f[i][j][k]);
add(f[i+1][j+1][k],f[i][j][k]*(m-j-k)%mod);
add(f[i+1][j+2][k],f[i][j][k]*C2(m-j-k)%mod);
if(j>=2) add(f[i+1][j-2][k+2],f[i][j][k]*C2(j)%mod);
if(j>=1) add(f[i+1][j-1][k+1],f[i][j][k]*j%mod),add(f[i+1][j][k+1],f[i][j][k]*(m-j-k)%mod*j%mod);
}
signed main()
{
freopen("chess.in","r",stdin); freopen("chess.out","w",stdout);
n=read(); m=read(); f[0][0][0]=1;
for(int i=0;i<n;i++) for(int j=0;j<=m;j++) for(int k=0;k<=m-j;k++) solve(i,j,k);
for(int i=0;i<=m;i++) for(int j=0;j<=m-i;j++) add(ans,f[n][i][j]);
printf("%lld",ans); return 0;
}
2021.11.18-NOIP模拟测试信心赛
2021.11.18-NOIP模拟信心赛
前言
太蒟蒻了,信心赛打的都快没信心了,giao
T1\\(\\color{green}100\\)题目
T1作为简单的签到题,直接先枚举所有的流量再用dijkstra跑这么多遍就可以了
#include<bits/stdc++.h>
#define M 2100
#define N 1100
#define inf 0x7f7f7f7f
using namespace std;
int n,m,maxx=-15;
int first[M],nex[M],to[M],w[M],f[M],tot;
int dis[N];
bool vis[N];
priority_queue<pair<int,int> >q;
//=======================================================
inline int read(){
int p=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c==\'-\')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-\'0\';c=getchar();}
return p*f;
}
//========================================================
inline void add(int x,int y,int z,int q){
nex[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
f[tot]=q;
}
//========================================================
inline void dijkstra(){
for(int x=1;x<=1000;x++){
for(int i=1;i<=n;i++){
dis[i]=inf;
vis[i]=0;
}
q.push(make_pair(0,1));
dis[1]=0;
while(!q.empty()){
int u=q.top().second;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=first[u];i;i=nex[i]){
int v=to[i];
if(f[i]<x)continue;
if(dis[u]+w[i]<dis[v]){
dis[v]=dis[u]+w[i];
if(!vis[v]){
q.push(make_pair(-dis[v],v));
}
}
}
}
if(dis[n]!=inf)
maxx=max(maxx,x*1000000/dis[n]);
}
}
//========================================================
int main(){
n=read(),m=read();
int a,b,c,f;
for(int i=1;i<=m;i++){
a=read(),b=read(),c=read(),f=read();
add(a,b,c,f);
add(b,a,c,f);
}
dijkstra();
cout<<maxx<<endl;
return 0;
}
T2\\(\\color{red}0\\)题目
明明第二天比第一题还简单简单我却做不出来
一开始在哪里想建图然后\\(bfs\\)然后小样例过了,大样例却挂了
结果直接用一个并查集分成两个集合就好了(有两种牛奶)
#include<bits/stdc++.h>
using namespace std;
#define N 100010
int n,m;
char s[N];
int f[N];
int ans[N];
//=====================================================
inline int read(){
int p=0,f=1;
char c;
while(!isdigit(c)){if(c==\'-\')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-\'0\';c=getchar();}
return p*f;
}
//======================================================
int gf(int x){
if(x == f[x]) return x;
return f[x] = gf(f[x]);
}
//======================================================
void hb(int x, int y){
f[gf(x)] = gf(y);
}
//======================================================
int main(){
n=read(),m=read();
scanf("%s",s+1);
int x,y;
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<n;i++){
x=read(),y=read();
if(s[x]==s[y])
hb(x,y);
}
for(int i=1;i<=m;i++){
char c;
x=read(),y=read(),cin>>c;
if(gf(x)==gf(y)&&s[x]!=c)ans[i]=0;
else ans[i]=1;
}
for(int i=1;i<=m;i++)cout<<ans[i];
return 0;
}
T3\\(\\color{red}0\\)题目
第二题都没做出来自然第三题也做不出来
题意:
求一颗树\\(u\\)到\\(v\\)的路径上有无某颜色的点
先跑一遍dfs,再求出\\(u\\)和\\(v\\)的最近公共祖先,这样就可以得到\\(u\\)和\\(v\\)的路径,然后用结构体和vector存储查询的颜色,最近公共祖先以及该点的编号,
最后再来一遍dfs,这一遍相当于把颜色按照节点的深度进行赋值,然后枚举点因为刚才记录了该点所要查询的颜色以及最近公共祖先,而且\\(u\\)和\\(v\\)都记录了,所以如果该点颜色深度大于最近公共祖先的深度,就说明一定能够经过,就把\\(ans[now.id]=1\\)
第三题代码复杂度\\(O\\)(\\(n\\log n\\))
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 100,M = 2e5 + 100;
const int maxdep=24;
int n,m;
int first[M],to[M],nex[M],tot;
int val[N],dep[N],faz[N][maxdep+1],ans[N];
struct mpair{
int col,top,id;
};
vector<mpair> vec[N];
//==============================================================
inline int read(){
int p=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c==\'-\')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-\'0\';c=getchar();}
return p*f;
}
//==============================================================
inline void add(int x,int y){
nex[++tot]=first[x];
first[x]=tot;
to[tot]=y;
}
//==============================================================
void dfs1(int u){
for(int i=1;i<=maxdep;i++)faz[u][i]=faz[faz[u][i-1]][i-1];
for(int i=first[u];i;i=nex[i]){
int v=to[i];
if(v==faz[u][0])continue;
dep[v]=dep[u]+1;
faz[v][0]=u;
dfs1(v);
}
}
//==============================================================
inline int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=maxdep;i>=0;i--)
if(dep[faz[x][i]]>=dep[y])x=faz[x][i];
if(x==y)return x;
for(int i=maxdep;i>=0;i--)
if(faz[x][i]!=faz[y][i])x=faz[x][i],y=faz[y][i];
return faz[x][0];
}
//==============================================================
int tmp[N];
void dfs(int u){
int lst=tmp[val[u]];
tmp[val[u]]=dep[u];
for(int i=0;i<vec[u].size();i++){
mpair now=vec[u][i];
if(tmp[now.col]>=dep[now.top])
ans[now.id]=1;
}
for(int i=first[u];i;i=nex[i]){
int v=to[i];
if(v==faz[u][0])continue;
dfs(v);
}
tmp[val[u]]=lst;
}
//==============================================================
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)val[i]=read();
int x,y;
for(int i=1;i<n;i++){
x=read(),y=read();
add(x,y);
add(y,x);
}
dfs1(1);
int c;
for(int i=1;i<=m;i++){
x=read(),y=read(),c=read();
int l=lca(x,y);
vec[x].push_back(mpair{c,l,i});
vec[y].push_back(mpair{c,l,i});
}
memset(tmp,-1,sizeof(tmp));
dfs(1);
for(int i=1;i<=m;i++)cout<<ans[i];
return 0;
}
今天就是集训的最后一天了,明天就是NOIP2021了,祝所有的OIer rp++
以上是关于NOIP模拟73的主要内容,如果未能解决你的问题,请参考以下文章