T1 排列
这道题目考察的主要是贪心的构造方法,唯一的难点就在于奇数的特殊处理
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int a[maxn],qs[maxn],qb[maxn];
int ans[maxn],ps,pb,ca,cb;
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int n;
ps=1; pb=1; ca=1; cb=1;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n/2;i++) qs[ca++]=i;
for(int i=n/2+1;i<=n;i++) qb[cb++]=i;
int flag=n%2;
for(int i=1;i<=n;i++)
{
if(a[i]<=(n+1)/2)
{
if(flag && a[i]==n/2+1 && ps<ca)
{
ans[i]=qs[ps++];
if(pb<cb)
qs[ca++]=qb[pb++];
continue;
}
else
ans[i]=qb[pb++];
flag=0;
}
else
ans[i]=qs[ps++];
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
T2 逆序对
这道题目考察的主要是对于期望的理解??
对于一个1-n的数列删除了i个数以后,可以离散后得到一个1-(n-i)的数列
删除了i次后的逆序对数量也就是1-(n-i)的数列的逆序对的数量
问题就转化为求1-n随机数列的逆序对数量 对于两个数 x,y它们二者构成逆序对的可能性为1/2,而总对数为
所以可以得到期望数量为
直接求个4的逆元一乘就做出来了
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll qpow(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1)
{
res=(res*a)%mod;
}
a=a*a%mod;
b>>=1;
}
return res;
}
ll inv4=qpow(4,mod-2);
ll calc(int x)
{
return ((1LL*x*(x-1)%mod)*inv4%mod);
}
int main()
{
int n;
scanf("%d",&n);
for(int i=n;i>=2;i--)
printf("%lld\n",calc(i));
printf("%d\n%d\n",0,0);
return 0;
}
T3 幸运路径
这道题目其实是树上差分很常见的一个思路——拆边
想要快速的计算一些边的和,我们把题目中的幸运边在两个点上+v,在两点之间的路径-v即可
然后就运用树上差分实现求点权和边权的和
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
typedef long long ll;
int n,m,q;
struct eedge
{
int to,nxt;
}e[maxn<<1];
int head[maxn],cnt;
void add(int x,int y)
{
e[++cnt].to=y;
e[cnt].nxt=head[x];
head[x]=cnt;
}
int lowbit(int x)
{
return x&(-x);
}
struct qzh
{
ll sum[maxn];
void add(int x,ll y)
{
while(x<=n)
{
sum[x]+=y;
x+=lowbit(x);
}
}
ll query(ll x)
{
ll res=0;
while(x>0)
{
res+=sum[x];
x-=lowbit(x);
}
return res;
}
}point,edge;
int f[maxn][20],dep[maxn],dfs_time,in[maxn],out[maxn];
void dfs(int u,int fa)
{
dep[u]=dep[fa]+1;
f[u][0]=fa;
in[u]=++dfs_time;
for(int i=head[u];i;i=e[i].nxt)
{
int to=e[i].to;
if(to==fa) continue;
dfs(to,u);
}
out[u]=dfs_time;
}
void init_lca()
{
for(int j=1;j<=18;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;i>=0;i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y) return x;
for(int i=18;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
ll sump[maxn],sume[maxn];
void calc(int u)
{
for(int i=head[u];i;i=e[i].nxt)
{
int to=e[i].to;
if(to==f[u][0]) continue;
sume[to]+=sume[u]; sump[to]+=sump[u];
calc(to);
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
int x,y,z;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfs(1,0); init_lca();
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
int lca=LCA(x,y);
point.add(in[x],-z);
point.add(in[y],-z);
point.add(in[lca],2*z);
edge.add(in[x],z);
edge.add(in[y],z);
edge.add(in[lca],-z);
if(f[lca][0])
edge.add(in[f[lca][0]],-z);
}
for(int i=1;i<=n;i++)
{
sume[i]=edge.query(out[i])-edge.query(in[i]-1);
sump[i]=point.query(out[i])-point.query(in[i]-1);
}
calc(1);
for(int i=1;i<=q;i++)
{
ll ans=0;
scanf("%d%d",&x,&y);
int lca=LCA(x,y);
ans=sume[x]+sume[y]-sume[lca]-sume[f[lca][0]];
ans+=sump[x]+sump[y]-2*sump[lca];
printf("%lld\n",ans);
}
return 0;
}
T4 优秀数
这道题目首先由数据范围很明显是和数位dp相关,再加上前面字符串的匹配问题可以发现可以AC自动机
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2005;
const int mod=1e9+7;
char s[maxn];
int n,tr[maxn][11],cnt=1,ans,mk[maxn],fail[maxn];
void insert(char *ss)
{
int len=strlen(ss+1),now=1;
for(int i=1;i<=len;i++)
{
int dig=ss[i]-'0';
if(!tr[now][dig]) tr[now][dig]=++cnt;
now=tr[now][dig];
}
mk[now]=1;
}
void build()
{
queue <int> q;
for(int i=0;i<=9;i++) tr[0][i]=1;
q.push(1);
while(!q.empty())
{
int u=q.front(); q.pop();
mk[u]|=mk[fail[u]];
for(int i=0;i<=9;i++)
if(tr[u][i])
{
fail[tr[u][i]]=tr[fail[u]][i];
q.push(tr[u][i]);
}
else tr[u][i]=tr[fail[u]][i];
}
}
int f[2][maxn][2];
void dp()
{
int m=strlen(s+1);
for(int i=1;i<=s[1]-'0';i++)
if(!mk[tr[1][i]])
f[1][tr[1][i]][i==s[1]-'0']++,f[1][tr[1][i]][i==s[1]-'0']%=mod;
for(int i=2;i<=m;i++)
{
memset(f[i&1],0,sizeof(f[i&1]));
for(int j=1;j<=9;j++)
if(!mk[tr[1][j]])
f[i&1][tr[1][j]][0]++,f[i&1][tr[1][j]][0]%=mod;
for(int j=1;j<=cnt;j++)
{
if(mk[j]) continue;
if(f[(i-1)&1][j][0])
for(int dig=0;dig<=9;dig++)
if(!mk[tr[j][dig]])
(f[i&1][tr[j][dig]][0]+=f[(i-1)&1][j][0])%=mod;
if(f[(i-1)&1][j][1])
for(int dig=0;dig<=s[i]-'0';dig++)
if(!mk[tr[j][dig]])
(f[i&1][tr[j][dig]][dig==s[i]-'0']+=f[(i-1)&1][j][1])%=mod;
}
}
for(int i=1;i<=cnt;i++)
if(!mk[i])
(((ans+=f[m&1][i][0])%=mod)+=f[m&1][i][1])%=mod;
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%s\n%d",s+1,&n);
char ss[maxn];
for(int i=1;i<=n;i++)
{
scanf("%s",ss+1);
insert(ss);
}
build();
dp();
printf("%d\n",ans);
return 0;
}