您的位置:首页 > 其它

NOIP模拟73

2021-10-10 21:25 99 查看 https://www.cnblogs.com/varuxn

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
;
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
,fa
;
int tot=1,head
,nxt[M<<1],ver[M<<1],edge[M<<1];
bool col
,vis
,can[M];
struct Road{int l,r,val;}pat[M];
priority_queue<pair<int,int> > q;
vector<pair<int,int> > pre
;
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
,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


;
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
[i][j]);
printf("%lld",ans); return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: