T1 树上的数
考场上比较脑瘫没有想到直接dfs就行了这样是O(n+m)的,傻不拉几地多添了个log,
不过因为accoder的评测机太弱了,绝大多数人的正解都是60分,所以没有什么差别;
直接dfs,d到不能d的点就return就好了
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,a,b;
const int mod=19760817, maxn=5e6+5;
int fa[maxn],q[maxn],sum[2];
int head[maxn],num;
struct edge{int to,nxt;}e[maxn<<1];
inline void add(int x,int y)
{e[++num]=(edge){y,head[x]};head[x]=num;}
bool vis[maxn];
inline void dfs(int x)
{
if(vis[x]) return;
vis[x]=1;++sum[1];
for(int i=head[x];i;i=e[i].nxt)
dfs(e[i].to);
}
signed main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();m=read();a=read();b=read();
q[1]=read(); int x=read(),y=read();
fa[2]=1; add(1,2); for(int i=3;i<=n;i++)
fa[i]=((1ll*fa[i-1]*a+b)^mod)%(i-1)+1,add(fa[i],i);
sum[1]=0;int ans=0; if(!vis[q[1]])dfs(q[1]);ans^=(n-sum[1]);
for(int i=2;i<=m;i++)
{
q[i]=(((1ll*q[i-1]*x+y)^mod)^(i<<1))%(n-1)+2;
dfs(q[i]);ans^=(n-sum[1]);
}
printf("%d\n",ans);
}
T2 时代的眼泪
看到这个题目的时候有点慌,以为是那个dio题,结果后来发现只是题目一样,一个比较显然的换根
将以x为根变为他的儿子y为根,他的变化量是在除了y的子树之外的点比y小的点的个数减去y的子树里比x小的数的个数
可以先dfs出来根为1的最终答案,然后用树状数组处理出对应变化量,最后再dfs一次换根就行了
复杂度O(n*log(n))。不过考场上我没有想到如何用树状数组解决这个问题,在dfs序上开了一棵主席树
复杂度也是O(nlog(n))的,不过常数比较大,再加上那个OJ比较慢,最后只有90分
主席树写法:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){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 maxn=1e6+5;
struct edge{int to,nxt;}e[maxn<<1];
int head[maxn],num,w[maxn],lsh[maxn],ext,n;
inline void add(int x,int y)
{
e[++num]=(edge){y,head[x]};head[x]=num;
e[++num]=(edge){x,head[y]};head[y]=num;
}
struct szsz{
#define lowbit(x) (x&(-x))
int c[maxn];
inline int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
inline void update(int x,int val){for(;x<=ext;x+=lowbit(x))c[x]+=val;}
}z;
int dfn[maxn],pp[maxn],cnt,siz[maxn];
long long ans[maxn];
inline void dfs1(int x,int f)
{
ans[1]=ans[1]+z.query(ext)-z.query(w[x]);
z.update(w[x],1);siz[x]=1;
dfn[x]=++cnt;pp[cnt]=x;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==f)continue;
dfs1(y,x); siz[x]+=siz[y];
} z.update(w[x],-1);
}
struct hjts{
int sum[maxn*23],root[maxn],cnt,ls[maxn*23],rs[maxn*23];
inline void update(int &rt1,int rt2,int l,int r,int pos)
{
rt1=++cnt; sum[rt1]=sum[rt2];
++sum[rt1]; if(l==r)return ;
ls[rt1]=ls[rt2];rs[rt1]=rs[rt2];
int mid=(l+r)>>1;
if(pos<=mid) update(ls[rt1],ls[rt2],l,mid,pos);
else update(rs[rt1],rs[rt2],mid+1,r,pos);
sum[rt1]=sum[ls[rt1]]+sum[rs[rt1]];
}
inline int query(int rt,int rt2,int l,int r,int ll,int rr)
{
if(ll>rr||rt==rt2)return 0;
if(l>=ll&&r<=rr) return sum[rt]-sum[rt2];
int mid=(l+r)>>1,res=0;
if(ll<=mid) res+=query(ls[rt],ls[rt2],l,mid,ll,rr);
if(rr>mid) res+=query(rs[rt],rs[rt2],mid+1,r,ll,rr);
return res;
}
inline int getval(int l,int r,int ll,int rr)
{
if(l>r||ll>rr)return 0;
return query(root[r],root[l-1],1,ext,ll,rr);
}
}tr;
inline void dfs2(int x,int f)
{
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to; if(y==f)continue;
int tmp1=tr.getval(1,dfn[y]-1,1,w[y]-1)+tr.getval(dfn[y]+siz[y],n,1,w[y]-1);
int tmp2=tr.getval(dfn[y],dfn[y]+siz[y]-1,1,w[x]-1);
ans[y]=ans[x]+tmp1-tmp2; dfs2(y,x);
}
}
signed main()
{
freopen("tears.in","r",stdin);
freopen("tears.out","w",stdout);
n=read();int q=read();
for(int i=1;i<=n;i++)
{
w[i]=read();
lsh[++ext]=w[i];
}
for(int i=1;i<n;i++)add(read(),read());
sort(lsh+1,lsh+1+ext);
ext=unique(lsh+1,lsh+1+ext)-lsh-1;
for(int i=1;i<=n;i++)w[i]=lower_bound(lsh+1,lsh+1+ext,w[i])-lsh;
dfs1(1,0);for(int i=1;i<=n;i++)
tr.update(tr.root[i],tr.root[i-1],1,ext,w[pp[i]]);
dfs2(1,0);int x; for(int i=1;i<=q;i++)
x=read(),printf("%lld\n",ans[x]);
}
树状数组写法:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){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 maxn=1e6+5;
struct edge{int to,nxt;}e[maxn<<1];
int head[maxn],num,w[maxn],lsh[maxn],ext,n;
inline void add(int x,int y)
{
e[++num]=(edge){y,head[x]};head[x]=num;
e[++num]=(edge){x,head[y]};head[y]=num;
}
struct szsz{
#define lowbit(x) (x&(-x))
int c[maxn];
inline int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
inline void update(int x,int val){for(;x<=ext;x+=lowbit(x))c[x]+=val;}
}z;
int dfn[maxn],pp[maxn],sum1[maxn],sum2[maxn],cnt,siz[maxn];
long long ans[maxn];
inline void dfs1(int x,int f)
{
z.update(w[x],1);siz[x]=1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==f)continue;
int jb=z.query(w[x]-1);
int bj=z.query(w[y]-1);
dfs1(y,x);
sum2[y]=z.query(w[y]-1)-bj;
sum1[y]=z.query(w[x]-1)-jb;
ans[1]+=sum2[y];
}
if(x==1)ans[1]+=z.query(w[1]-1);
}
inline void dfs2(int x,int f)
{
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to; if(y==f)continue;
ans[y]=ans[x]-sum1[y]+z.query(w[y]-1)-sum2[y];
dfs2(y,x);
}
}
signed main()
{
freopen("tears.in","r",stdin);
freopen("tears.out","w",stdout);
n=read();int q=read();
for(int i=1;i<=n;i++)
{
w[i]=read();
lsh[++ext]=w[i];
}
for(int i=1;i<n;i++)add(read(),read());
sort(lsh+1,lsh+1+ext);
ext=unique(lsh+1,lsh+1+ext)-lsh-1;
for(int i=1;i<=n;i++)w[i]=lower_bound(lsh+1,lsh+1+ext,w[i])-lsh;
dfs1(1,0); dfs2(1,0);
int x; for(int i=1;i<=q;i++)
x=read(),printf("%lld\n",ans[x]);
}
T3 传统异能
这个题挺不错的,由于我自己之前只会O(n)求不同子序列个数的写法,所以没有想到可以用矩阵;
先说一下n^2的做法,我们可以设dp[i][1/0] 为第i个字符选或者不选的能够构成的子序列的个数
lst[c]为c这个字符上一次出现的位置,那么转移的话就是:
dp[i][1]=(dp[i-1][1]+dp[i-1][0]+1)%mod;
dp[i][0]=(dp[i-1][1]+dp[i-1][0])%mod;
if(lst[a[i]]) dp[i][0]=(dp[i][0]+mod-dp[lst[a[i]]][1])%mod;
当然,如果你写了这个式子之后就会很难想到正解,因为这个式子有减去的地方,会很难处理
所以我们就要用一种复杂度稍微高一些的做法来想到正解
我们设dp[i][j]为前i个字符里面,以j结尾的子序列有多少个,
那么转移的话就是
if(j==s[i]) dp[i][j]=dp[i][1]+dp[i][2]+dp[i][3]+1;
else dp[i][j]=dp[i-1][j];
我觉得还是比较好理解的,我们假如让所有的以c为结尾的子序列再加上一个c
那么现在以c结尾的子序列的最小长度都为2,那么再加上让以a和b结尾的子序列加一个c
就又补回来了,最后再加1表示前面一个都不被选择,只选它自己
我们发现这是一个很明显的矩形式子,因为字符集大小只有3,所以我们可以构建三个不同的矩阵
第1个矩阵大概长这个亚子
1 0 0 0
1 1 1 1
0 0 1 0
0 0 0 1
对角线显然都是1,然后第i行是1,嗯就这样
然后我们可以用线段树维护一下每个点对应的矩形,以及区间乘积是多少 相信大家都知道矩形支持交换律
查询完之后再让答案矩形乘一个这个矩阵
1 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
将第一列的加起来就是答案了
#include<bits/stdc++.h>
#define int long long
#define lid id<<1
#define rid id<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){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 mod=998244353;
const int maxn=1e5+5;
struct matrix{
int mat[5][5];
inline void init(){
memset(mat,0,sizeof(mat));
for(int i=0;i<=3;i++)mat[i][i]=1;
}
inline void clear(){memset(mat,0,sizeof(mat));}
inline void print()
{
for(int i=0;i<=3;i++)
{
for(int j=0;j<=3;j++)
cout<<mat[i][j]<<" ";
cout<<endl;
}
}
inline matrix operator*(const matrix &a)const{
matrix c;c.clear();
for(int i=0;i<=3;i++) for(int j=0;j<=3;j++) for(int k=0;k<=3;k++)
(c.mat[i][j]=a.mat[i][k]*mat[k][j]%mod+c.mat[i][j])%=mod;
return c;
}
}tmp[4];
int n,m;
inline int id(char c){return c-'A'+1;}
char s[maxn];int a[maxn];
matrix sum[maxn<<2];
inline void init()
{
tmp[1].init();tmp[2].init();tmp[3].init();
for(int i=1;i<=3;i++) for(int j=0;j<=3;j++)
tmp[i].mat[i][j]=1;tmp[0].clear();
tmp[0].mat[0][0]=1;
}
inline void build(int id,int l,int r)
{
if(l==r) return sum[id]=tmp[a[l]],void();
int mid=(l+r)>>1;
build(lid,l,mid);build(rid,mid+1,r);
sum[id]=sum[lid]*sum[rid];
}
inline void update(int id,int l,int r,int pos)
{
if(l==r) return sum[id]=tmp[a[pos]],void();
int mid=(l+r)>>1;
if(pos<=mid) update(lid,l,mid,pos);
else update(rid,mid+1,r,pos);
sum[id]=sum[lid]*sum[rid];
}
inline matrix query(int id,int l,int r,int ll,int rr)
{
if(l>=ll&&r<=rr)return sum[id];
int mid=(l+r)>>1;matrix a;a.init();
if(ll<=mid) a=query(lid,l,mid,ll,rr);
if(rr>mid) a=a*query(rid,mid+1,r,ll,rr);
return a;
}
signed main()
{
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
n=read();m=read(); scanf("%s",s+1);
for(int i=1;i<=n;i++) a[i]=id(s[i]);
init();build(1,1,n);
while(m--)
{
int type=read();
if(type==2)
{
int ll=read(),rr=read();
matrix ans=query(1,1,n,ll,rr);
ans=tmp[0]*ans;int sum=0;
sum=(ans.mat[1][0]+ans.mat[2][0]+ans.mat[3][0])%mod;
printf("%lld\n",sum);
}
else
{
int pos=read();char c;cin>>c;
a[pos]=id(c);update(1,1,n,pos);
}
}
}
T4 铺设道路
我们假设第0块和第n+1块的高度为0,那么显然此时的答案不会改变
那么,我们首先可以先将其差分一下,我们将每个点的高度干到0,等价于将差分数组全部干到0
对于i>=1&&i<=n来说 我们设b[i]为d[i]-d[i-1],即差分数组
那么显然最短时间就是sigma(max(b[i],0));
对于b[l]和b[r],假设我们对l到r-1(r>l)之间施一次工,那么b[l]会-1,b[r]会+1
那么我们考虑一个贪心思想,如果我们想要总体力是最大的,那么我们应该让每个小于0的b[r]匹配的b[l]越远越好
最小的则反之,我们从1枚举每个b[i],如果b[i]>0就将其压入队列,否则就取队尾或者队首进行操作
复杂度O(n);
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){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 mod=1e9+7;
const int maxn=3e5+5;
int n,d[maxn],b[maxn];
deque<int >q;
signed main()
{
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
n=read();int ans=0;
for(int i=1;i<=n;i++)
{
d[i]=read();b[i]=d[i]-d[i-1];
ans+=max(0ll,d[i]-d[i-1]);
}
printf("%lld\n",ans);
int maxx=0,minn=0;n++;b[n]=-d[n-1];
for(int i=1;i<=n;i++)
{
if(b[i]>0) q.push_back(i);
else while(b[i])
{
int x=q.back();
if(abs(b[i])>=abs(b[x]))
{
q.pop_back();
(maxx+=(i-x)*(i-x)%mod*b[x]%mod)%=mod;
b[i]+=b[x];b[x]=0;
}
else
{
(maxx+=(i-x)*(i-x)%mod*(-b[i])%mod)%=mod;
b[x]+=b[i];b[i]=0;
}
}
}
for(int i=1;i<=n;i++) b[i]=d[i]-d[i-1];
printf("%lld\n",maxx);
for(int i=1;i<=n;i++)
{
if(b[i]>0) q.push_back(i);
else while(b[i])
{
int x=q.front();
if(abs(b[i])>=abs(b[x]))
{
q.pop_front();
(minn+=(i-x)*(i-x)%mod*b[x]%mod)%=mod;
b[i]+=b[x];b[x]=0;
}
else
{
(minn+=(i-x)*(i-x)%mod*(-b[i])%mod)%=mod;
b[x]+=b[i];b[i]=0;
}
}
}
printf("%lld\n",minn);
}
总结::
距离noip2021不远了,csp的失利一直缠绕再我心头,不过我是不会放弃的,加油吧JYF!!!!!!!