2021 10.26 模拟测试
\(\mathrm{T1}\)
\(\mathrm{Solution}\)
我们可以记录一个\(cnt[x]\)表示到第\(i\)个操作要让答案是\(x\)的最小操作数。
显然对于操作1等同于将\(cnt[p]\)清零,将除\(p\)以外的所有\(cnt++\)
而对于操作2则是将\(cnt[q]\)更新为\(\min(cnt[p],cnt[q])\),而\(cnt[p]++\)
但这样做是\(O(n^2)\)的,所以只需要把操作1的区间加法优化为O(1)即可
#include<bits/stdc++.h>
#define fi first
#define se second
#define in read()
using namespace std;
inline int read()
{
int data=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+48);
}
const int N=1e7+5;
int n,lzy;
int cnt[N],lazy[N];
int main()
{
//freopen("order.in","r",stdin);
//freopen("order.out","w",stdout);
n=in;
memset(cnt,-1,sizeof(cnt));
cnt[1]=0;
for(int i=1;i<=n;i++)
{
int op=in;
if(op==1)
{
lzy++;
// for(int i=1;i<=n;i++)
// if(~cnt[i]) cnt[i]++;
int p=in;
cnt[p]=0;lazy[p]=lzy;
}
if(op==2)
{
int p=in,q=in;
if(~cnt[p]&&((cnt[q]+lzy-lazy[q])>(cnt[p]+lzy-lazy[p])||cnt[q]==-1))
cnt[q]=cnt[p],lazy[q]=lazy[p];
if(~cnt[p]) cnt[p]++;
}
// cout<<1<<'\n';
// cout<<cnt[2]<<'\n';
}
for(int i=1;i<=n;i++) //write(cnt[i]),putchar(' ');
write(~cnt[i]?cnt[i]+lzy-lazy[i]:-1),putchar(' ');
}
/*
5
2 1 4
1 3
2 3 5
1 2
1 5
*/
\(\mathrm{T2}\)
\(\mathrm{Solution}\)
对于暴力枚举点是\(O(n^2)\)的,但是发现每一个点对答案的贡献只有自己的度数减去与其他点的连的边的个数,而度数最多只有\(\sqrt{m}\)个,所以如果我们先不考虑重边而去枚举度数的话效率是\(O(m)\)的,最后在枚举点去把重复的点删掉即可
效率\(O(n+m)\)
为什么最多只有\(\sqrt{m}\)个呢,因为对于所有点来说度数之和为\(2m\),而最坏情况就是\(1+2+……+k=2m\),解得\(k\)约等于\(\sqrt{m}\)
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define int long long
#define in read()
using namespace std;
inline int read()
{
int data=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+48);
}
const int N=1e6+5;
int n,m,Q,rd[N],t[N];
int a[N<<1];
vector<int>e[N];
vector<pii>c;
signed main()
{
n=in,m=in;
for(int i=1,u,v;i<=m;i++)
{
u=in,v=in;
e[u].push_back(v);
if(u!=v) e[v].push_back(u);
}
for(int i=1;i<=n;i++) t[rd[i]=e[i].size()]++;
for(int i=0;i<=m;i++) if(t[i]>0) c.push_back(mp(i,t[i]));
for(int i=0;i<c.size();i++)
for(int j=i;j<c.size();j++)
if(i==j) a[c[i].fi+c[i].fi]+=(c[i].se*(c[i].se-1))>>1;
else a[c[i].fi+c[j].fi]+=c[i].se*c[j].se;
for(int u=1;u<=n;u++)
{
static int cnt[N];
for(int v:e[u])
if(v<u) cnt[v]++;
for(int v:e[u])
if(cnt[v])
{
a[rd[u]+rd[v]]--;
a[rd[u]+rd[v]-cnt[v]]++;
cnt[v]=0;
}
}
for(int i=m<<1;~i;--i) a[i]+=a[i+1];
Q=in;
while(Q--) write(a[in+1]),puts("");
}
\(\mathrm{T3}\)
\(\mathrm{Solution}\)
设\(dp[i][mov]\)表示考虑第\(i\)个到第\(n\)个操作,能有至少一次到当前位置\(+mov\)的概率
转移即
\[dp[i][mov]+=p\times dp[i+1][mov]
\]
\[dp[i][mov+a[i]]+=(1-p)\times dp[i+1][mov]
\]
\[dp[i][0]=1
\]
最后\(dp[1][i]\)就是答案
#include<bits/stdc++.h>
#define in read()
using namespace std;
inline int read()
{
int data=0,w=1; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int mod=998244353;
const int N=5e3+5;
const int _100=828542813;
int n,p,a[N];
int f[N][N<<1];
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(int a,int b){return (1ll*a*b)%mod;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
void solve()
{
f[n+1][N]=1;int l=0,r=0;
for(int i=n;i;--i)
{
a[i]==1?r++:l++;
for(int j=N-l;j<=N+r;j++)
{
f[i][j]=add(f[i][j],mul(p,f[i+1][j]));
f[i][j+a[i]]=add(f[i][j+a[i]],mul(dec(1,p),f[i+1][j]));
}
f[i][N]=1;
}
for(int i=N-n;i<=N+n;i++)
write(f[1][i]),puts("");
}
int main()
{
n=in,p=mul(in,_100);
for(int i=1;i<=n;i++) a[i]=in;
puts("0");solve();
}

浙公网安备 33010602011771号