AHUACM 第一场摸底测试复盘题解
A P4838 P哥破解密码
f[i][j]表示前i个位置结尾有j个A的合法串数量,j为0,1,2
矩阵快速幂板子
#include<bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
const int mod = 19260817;
int f[N][3];
struct juzhen
{
int n,m;
int a[3][3];
juzhen(int n=0,int m=0):n(n),m(m)
{
memset(a,0,sizeof(a));
}
};
juzhen mul(juzhen &a,juzhen &b)
{
juzhen c(a.n,b.m);
for(int i=0;i<a.n;i++)
{
for(int j=0;j<b.m;j++)
{
for(int k=0;k<a.m;k++)
{
c.a[i][j]=(c.a[i][j]+1ll*a.a[i][k]*b.a[k][j]%mod)%mod;
}
}
}
return c;
}
void solve(int T)
{
juzhen A(1,3),B(3,3),S(3,3);
A.a[0][0]=A.a[0][1]=1;
B.a[0][0]=1;B.a[0][1]=1;B.a[0][2]=0;
B.a[1][0]=1;B.a[1][1]=0;B.a[1][2]=1;
B.a[2][0]=1;B.a[2][1]=0;B.a[2][2]=0;
S.a[0][0]=1;S.a[1][1]=1;S.a[2][2]=1;
T--;
while(T)
{
if(T&1)
{
S=mul(S,B);
}
B=mul(B,B);
T>>=1;
}
A=mul(A,S);
cout<<(A.a[0][0]+A.a[0][1]+A.a[0][2])%mod<<endl;
}
int main()
{
int T;
cin>>T;
while(T --> 0)
{
int x;
cin>>x;
solve(x);
}
return 0;
}
B P5142 区间方差
化简后变为\(\frac{1}{n}{\sum{x_i^2}}-\bar{x}^2\)
用线段树维护区间和 以及 区间平方和 ,支持单点修改区间查询
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int mod = 1e9+7;
const int N = 3e5 + 10;
struct linetree
{
LL sum1,sum2;
}t[N<<2];
LL a[N];
void pushup(int p)
{
t[p].sum1 = (t[p*2].sum1 + t[p*2+1].sum1)%mod;
t[p].sum2 = (t[p*2].sum2 + t[p*2+1].sum2)%mod;
}
void build(int p,int l,int r)
{
if(l==r)
{
t[p].sum1 = a[l];
t[p].sum2 = a[l] * a[l]%mod;
return;
}
int mid = (l + r) >> 1;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
pushup(p);
}
void update(int p,int l,int r,int x,LL v)
{
if(l==r)
{
t[p].sum1=v;
t[p].sum2=v*v%mod;
return;
}
int mid = (l + r) >> 1;
if(x <= mid)
update(p*2, l, mid, x, v);
else
update(p*2+1, mid+1, r, x, v);
pushup(p);
}
LL find1(int p,int l,int r,int L,int R)
{
if(L <= l && r <= R)
return t[p].sum1;
int mid = (l + r) >> 1;
LL sum = 0;
if(L <= mid)
sum += find1(p*2, l, mid, L, R);
if(R > mid)
sum += find1(p*2+1, mid+1, r, L, R);
return sum % mod;
}
LL find2(int p,int l,int r,int L,int R)
{
if(L <= l && r <= R)
return t[p].sum2;
int mid = (l + r) >> 1;
LL sum = 0;
if(L <= mid)
sum += find2(p*2, l, mid, L, R);
if(R > mid)
sum += find2(p*2+1, mid+1, r, L, R);
return sum % mod;
}
LL qpow(LL a,LL b=mod-2)
{
LL s=1;
while(b)
{
if(b&1)
s=s*a%mod;
a=a*a%mod;
b>>=1;
}
return s;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
build(1,1,n);
for(int i=1;i<=m;i++)
{
int c,x,y;
cin>>c>>x>>y;
if(c==1)
{
update(1,1,n,x,y);
}else
{
LL ans=0;
LL sum1=find1(1,1,n,x,y);
LL sum2=find2(1,1,n,x,y);
ans=qpow(y-x+1)*sum2%mod;
sum1=qpow(y-x+1)*sum1%mod;
ans=(ans-sum1*sum1%mod+mod)%mod;
cout<<ans<<endl;
}
}
return 0;
}
C [NOIP 2013 提高组] 货车运输
从u到v的路径上最小值最大的路径一定在最大生成树上可以找到。
证明:假设最大生成树上不存在能使最小值最大的路径,设最小值对应的边为e1,两边的点为u1,u2,那么在最大生成树上的u1,u2之间连一条边,最大生成树上路径的最小值一定在换上,此时将其切掉会产生一个更大的生成树,不符合最大生成树定义。
求个最大生成树+倍增lca即可
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
struct Dsu
{
int n;
int fa[maxn];
int find(int x) {
if (fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
void merge(int x, int y) {
int fx = find(x), fy = find(y);
if (fx != fy) fa[fx] = fy;
}
bool check(int x, int y) {
return find(x) == find(y);
}
void init(int n) {
this->n = n;
for (int i = 1; i <= n; i++) fa[i] = i;
}
}D;
struct Edge {
int x,y,w;
}a[maxn];
bool cmp(Edge a, Edge b) {
return a.w > b.w;
}
vector<pair<int,int>>e[maxn];
int f[maxn][20],fa[maxn][20],dep[maxn];
void dfs(int u)
{
for(auto i:e[u])
{
int v=i.first;
if(v==fa[u][0]) continue;
fa[v][0] = u;
dep[v]=dep[u]+1;
f[v][0] = i.second;
dfs(v);
}
}
void init(int n)
{
for(int j=1;j<20;j++)
{
f[0][j]=1e9;
for(int i=1;i<=n;i++)
{
fa[i][j] = fa[fa[i][j-1]][j-1];
f[i][j] = min(f[i][j-1], f[fa[i][j-1]][j-1]);
}
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
int d = dep[x] - dep[y];
int ans=1e9;
for(int i=0;i<20;i++)
{
if(d&(1<<i)) ans=min(ans, f[x][i]),x = fa[x][i];
}
for(int i=19;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
ans=min(ans, min(f[x][i], f[y][i]));
x = fa[x][i];
y = fa[y][i];
}
}
return x == y ? ans : min(ans, min(f[x][0], f[y][0]));
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>a[i].x>>a[i].y>>a[i].w;
}
sort(a+1, a+m+1, cmp);
D.init(n);
for(int i=1;i<=m;i++)
{
if(D.check(a[i].x,a[i].y))continue;
e[a[i].x].push_back({a[i].y, a[i].w});
e[a[i].y].push_back({a[i].x, a[i].w});
D.merge(a[i].x, a[i].y);
}
f[0][0]=1e9;
for(int i=1;i<=n;i++)
{
if(!dep[i]) {
f[i][0]=1e9;
dfs(i);
}
}
init(n);
int q;
cin>>q;
while(q--) {
int x,y;
cin>>x>>y;
if(!D.check(x,y)) {
cout << -1 << endl;
continue;
}
cout << lca(x,y) << endl;
}
return 0;
}
D P2804 神秘数字
数位dp的板子题,三年没做过了,也算是当复健吧
整体还是一个递归的思想。
0对答案没有贡献,不需要考虑
计算[0,A1A2A3……An] 中每一个数字的出现次数,可以将其分成高位贡献和其他位的贡献,也就是下面三个部分:
(1)[0,A1 00……0)中各位数位数字出现的总和
(2)A2A3……An个A1 数字
(3)[0,A2A3……An] 中各位数字的总和
(1)部分:对于数位i,其出现次数为:A1*f[n-1]+tenn-1
(2)部分 :A2A3……An+1次的数位A1
(3)部分 递归解决
#include<bits/stdc++.h>
using namespace std;
#define LL unsigned long long
const LL mod = 1e9+7;
LL f[21],ten[21];
void init()
{
ten[0]=1;
for(int i=1;i<=18;i++)
{
ten[i]=ten[i-1]*10;
f[i]=10*f[i-1]+ten[i-1];
}
}
LL cnt[10],num[21],ct[10];
void solve(LL x)
{
LL n=0,y=x;
while(y)
{
num[++n]=y%10;
y/=10;
}
for(int i=0;i<10;i++)
cnt[i]=0;
for(int i=n;i>=1;i--)
{
for(int j=0;j<10;j++)
{
cnt[j]+=f[i-1]*num[i];
}
for(int j=1;j<num[i];j++)
{
cnt[j]+=ten[i-1];
}
x-=num[i]*ten[i-1];
cnt[num[i]]+=x+1;
}
}
int main()
{
init();
solve(9);
for(int i=0;i<10;i++)
cout<<i<<" : "<<cnt[i]<<endl;
return 0;
}
E P2326 AKN’s PPAP
求数列中两数与的最大值
按位比较,初始时所有数都在集合中,从高位向低位扫,如果这一位有值的数不超过1个,直接跳过,恰好两个,得出答案;大于2个,这一位不存在的数直接out
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 200005;
LL a[maxn],ans;
const LL mod = 92084931;
void merge_sort(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
merge_sort(l,mid);
merge_sort(mid+1,r);
vector<LL> b, c;
for(int i=l;i<=mid;i++) b.push_back(a[i]);
for(int i=mid+1;i<=r;i++) c.push_back(a[i]);
int i=0,j=0;
while(i<b.size() && j<c.size())
{
if(b[i]<c[j])
{
a[l+i+j]= b[i];
i++;
}
else
{
ans+=b.size()-i;
a[l+i+j]= c[j];
j++;
}
}
while(i<b.size())
{
a[l+i+j]= b[i];
i++;
}
while(j<c.size())
{
a[l+i+j] = c[j];
j++;
}
}
int main()
{
int n,M;
cin>>n>>M;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
a[i]-=M;
a[i]+=a[i-1];
}
merge_sort(0,n);
cout<<(1ll*n*(n+1)/2-ans+mod)%mod<<endl;
return 0;
}
F P2804 神秘数字
将数列a中元素全减去M,条件就变成了区间和大于0
此时再做个前缀和,答案就变成了统计前缀和个数
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 200005;
LL a[maxn],ans;
const LL mod = 92084931;
void merge_sort(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
merge_sort(l,mid);
merge_sort(mid+1,r);
vector<LL> b, c;
for(int i=l;i<=mid;i++) b.push_back(a[i]);
for(int i=mid+1;i<=r;i++) c.push_back(a[i]);
int i=0,j=0;
while(i<b.size() && j<c.size())
{
if(b[i]<c[j])
{
a[l+i+j]= b[i];
i++;
}
else
{
ans+=b.size()-i;
a[l+i+j]= c[j];
j++;
}
}
while(i<b.size())
{
a[l+i+j]= b[i];
i++;
}
while(j<c.size())
{
a[l+i+j] = c[j];
j++;
}
}
int main()
{
int n,M;
cin>>n>>M;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
a[i]-=M;
a[i]+=a[i-1];
}
merge_sort(0,n);
cout<<(1ll*n*(n+1)/2-ans+mod)%mod<<endl;
return 0;
}
浙公网安备 33010602011771号