B~F Java题解,代码已去除冗余~~~
固定转折列后,求和结果即为左半边的上一行加上右半边的下一行,而交换的情况有多种:交换在同一侧,无变化,交换在转换列的两侧,需要分别计算前后缀的最大交换贡献;而就还有一种情况是,交换的其中一列就是转折列,,,以上三种情况分别在枚举转折列即可,时间复杂度O(Tn)
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
for(int i=sc.nextInt();i!=0;i--){
int n=sc.nextInt(),a[][]=new int[n+5][2];
for(int k=0;k<2;k++){
for(int j=1;j<=n;j++){
a[j][k]=sc.nextInt();
}
}
//计算前后缀之和,计算一些列前缀,后缀最大值
long pre[]=new long[n+5],suf[]=new long[n+5],maxPreSum[]=new long[n+5],maxSufSum[]=new long[n+5],maxPre[]=new long[n+5],maxSuf[]=new long[n+5],ans=-(long)1e18;
maxPreSum[0]=maxPre[0]=maxSuf[n+1]=maxSufSum[n+1]=-(long)1e18;
for(int j=1;j<=n;j++){
pre[j]=pre[j-1]+a[j][0];
suf[n+1-j]=suf[n+2-j]+a[n+1-j][1];
maxPreSum[j]=Math.max(maxPreSum[j-1],a[j][1]);
maxSufSum[n+1-j]=Math.max(maxSufSum[n+2-j],a[n+1-j][0]);
maxPre[j]=Math.max(maxPre[j-1],a[j][1]-a[j][0]);
maxSuf[n+1-j]=Math.max(maxSuf[n+2-j],a[n+1-j][0]-a[n+1-j][1]);
}
//枚举转折点:
for(int j=1;j<=n;j++){
long cur=pre[j]+suf[j];
//无交换
ans=Math.max(ans,cur);
//交换前后两列:
ans=Math.max(ans,cur+maxPre[j-1]+maxSuf[j+1]);
//交换前边与转折点:
ans=Math.max(ans,cur-a[j][1]+maxPreSum[j-1]);
//交换后边与转折点:
ans=Math.max(ans,cur-a[j][0]+maxSufSum[j+1]);
}
System.out.println(ans);
}
}
}
手动计算前几项,即可知道每个数的贡献即为自身呈上斐波那契数列前若干项,因此直接用前缀和相乘即可,时间复杂度(Tn+1e6)
import java.util.*;
public class Main{
static long facPre[]=new long[(int)1e6+5],mod=998244353;
static {
facPre[1]=1;
for(int i=2;i<=(int)1e6+2;i++){
facPre[i]=(facPre[i-1]+facPre[i-2])%mod;
}
for(int i=2;i<=(int)1e6+2;i++){
facPre[i]=(facPre[i]+facPre[i-1])%mod;
}
}
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
for(int i=sc.nextInt();i!=0;i--){
int n=sc.nextInt();
long ans=0;
for(int j=1;j<=n;j++){
ans+=sc.nextInt()*(1+facPre[Math.max(0,n-j-1)]+facPre[j-1])%mod;
}
System.out.println(ans%mod);
}
}
}
大多数边都是全覆盖的,因此最基本的邻接表可以先用弗洛伊德算法算出来,而最多有q个边石需要区间覆盖的,因此可以使用延迟更新线段树分割时间轴,每次更新区间的时候,利用父节点的邻接矩阵,加上没被跟新过且全覆盖本区间的边,时间复杂度O(T(n^3+n^2qlogn))
import java.util.*;
public class Main{
static long mod=998244353,inf=(long)1e18;
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
for(int i=sc.nextInt();i!=0;i--){
int n=sc.nextInt(),m=sc.nextInt(),q=sc.nextInt();
SegTree st[]=new SegTree[q<<2|3];
st[1]=new SegTree(0,q);
st[1].dis=new long[n+1][n+1];
//[start,end,x,y,w]
for(int j=0;j<m;j++){
int u=sc.nextInt(),v=sc.nextInt(),w=sc.nextInt();
st[1].edges.add(new int[]{0,q,u,v,w});
}
for(int j=1;j<=n;j++){
Arrays.fill(st[1].dis[j],inf);
st[1].dis[j][j]=0;
}
build(st,1);
List<Integer> list=new ArrayList<>();
for(int j=1;j<=q;j++){
int op=sc.nextInt();
if(op==1){
int x=sc.nextInt(),y=sc.nextInt(),w=sc.nextInt();
st[1].edges.add(new int[]{j,q,x,y,w});
}
else if(op==2){
st[1].edges.get(sc.nextInt()-1)[1]=j;
}
else{
list.add(j);
}
}
//将全覆盖的边更新:
for(int e[]:st[1].edges){
if(e[0]==0&&e[1]==q){
st[1].dis[e[2]][e[3]]=st[1].dis[e[3]][e[2]]=Math.min(st[1].dis[e[3]][e[2]],e[4]);
}
}
//弗洛伊德:
for(int d=1;d<=n;d++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
st[1].dis[j][k]=Math.min(st[1].dis[j][k],st[1].dis[j][d]+st[1].dis[d][k]);
}
}
}
st[1].edges=find(st[1].edges,q);
for(int a:list){
System.out.println(find(st,1,a));
}
}
}
static long find(SegTree st[],int idx,int a){
int l=st[idx].l,r=st[idx].r;
if(l==r){
long ans=0;
for(int i=1;i<st[idx].dis.length;i++){
for(int j=1;j<st[idx].dis.length;j++){
ans+=(st[idx].dis[i][j]==inf?0:st[idx].dis[i][j]);
}
}
return ans%mod;
}
clear(st,idx,a<=(l+r)/2?0:1);
return find(st,idx<<1|(a<=(l+r)/2?0:1),a);
}
static void clear(SegTree st[],int idx,int next){
if(st[idx].l<st[idx].r&&st[idx<<1|next].dis==null){
int n=st[idx].dis.length;
st[idx<<1|next].dis=new long[n][];
for(int i=1;i<n;i++){
st[idx<<1|next].dis[i]=st[idx].dis[i].clone();
}
for(int a[]:st[idx].edges){
if(a[0]!=-1&&!(a[0]>st[idx<<1|next].r||a[1]<st[idx<<1|next].l)){
st[idx<<1|next].edges.add(a.clone());
}
}
update(st[idx<<1|next]);
}
}
static void update(SegTree st){
int l=st.l,r=st.r;
for(int e[]:st.edges){
if(e[0]<=l&&e[1]>=r){
e[0]=-1;
for(int i=1;i<st.dis.length;i++){
for(int j=1;j<st.dis.length;j++){
st.dis[i][j]=Math.min(st.dis[i][j],Math.min(st.dis[i][e[2]]+st.dis[e[3]][j],st.dis[i][e[3]]+st.dis[e[2]][j])+e[4]);
}
}
}
}
}
static void build(SegTree st[],int idx){
int l=st[idx].l,r=st[idx].r;
if(l<r){
int mid=(l+r)>>1;
st[idx<<1]=new SegTree(l,mid);
st[idx<<1|1]=new SegTree(mid+1,r);
build(st,idx<<1);
build(st,idx<<1|1);
}
}
static List<int[]> find(List<int[]> list,int q){
//将全覆盖的边去除
List<int[]> ans=new ArrayList<>();
for(int a[]:list){
if(a[0]!=0||a[1]!=q){
ans.add(a);
}
}
return ans;
}
}
class SegTree{
int l,r;
long dis[][];
List<int[]> edges=new ArrayList<>();
SegTree(int l,int r){
this.l=l;
this.r=r;
}
}
可启发式合并集合,在合并时,若是需要将大集合合并到小集合中,需要反向操作,并更改集合标记,因此实合并此时最多为n次,记录交集可用集合标记组合,时间复杂度T(nlogn+q)
import java.util.*;
import java.io.*;
public class Main{
public static void main(String args[]) throws IOException{
BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
for(int i=Integer.parseInt(bf.readLine());i!=0;i--){
//group表示的是,某个元素实际上指向哪个位置,idx表示的是,每个组实际上指向哪个位置
int n=Integer.parseInt(bf.readLine()),groupA[]=new int[n+5],groupB[]=new int[n+5],idxA[]=new int[n+5],idxB[]=new int[n+5];
List<Integer> listA[]=new List[n+5],listB[]=new List[n+5];
for(int j=1;j<=n;j++){
listA[j]=new ArrayList<>();
listB[j]=new ArrayList<>();
idxA[j]=idxB[j]=j;
}
StringTokenizer st=new StringTokenizer(bf.readLine());
for(int j=1;j<=n;j++){
int a=Integer.parseInt(st.nextToken());
groupA[a]=j;
listA[j].add(a);
}
st=new StringTokenizer(bf.readLine());
for(int j=1;j<=n;j++){
int b=Integer.parseInt(st.nextToken());
groupB[b]=j;
listB[j].add(b);
}
Map<Long,Integer> map=new HashMap<>();
for(int j=1;j<=n;j++){
inc(map,(long)groupA[j]*(n+5)+groupB[j],1);
}
for(int j=Integer.parseInt(bf.readLine());j!=0;j--){
st=new StringTokenizer(bf.readLine());
int op=Integer.parseInt(st.nextToken()),idxI=Integer.parseInt(st.nextToken()),idxJ=Integer.parseInt(st.nextToken());
if(op==1){
bw.write(map.getOrDefault((long)idxA[idxI]*(n+5)+idxB[idxJ],0)+"\n");
}
else if(op==2){
int p1=idxA[idxI],p2=idxA[idxJ];
//把小的集合都清空,元素全部放到大的集合里
if(listA[p1].size()<listA[p2].size()){
for(int a:listA[p1]){
listA[p2].add(a);
inc(map,(long)p1*(n+5)+groupB[a],-1);
inc(map,(long)p2*(n+5)+groupB[a],1);
groupA[a]=p2;
}
listA[p1].clear();
idxA[idxI]=p2;
idxA[idxJ]=p1;
}
else{
for(int a:listA[p2]){
listA[p1].add(a);
inc(map,(long)p2*(n+5)+groupB[a],-1);
inc(map,(long)p1*(n+5)+groupB[a],1);
groupA[a]=p1;
}
listA[p2].clear();
}
}
else{
int p1=idxB[idxI],p2=idxB[idxJ];
//把小的集合都清空,元素全部放到大的集合里
if(listB[p1].size()<listB[p2].size()){
for(int a:listB[p1]){
listB[p2].add(a);
inc(map,(long)groupA[a]*(n+5)+p1,-1);
inc(map,(long)groupA[a]*(n+5)+p2,1);
groupB[a]=p2;
}
listB[p1].clear();
idxB[idxI]=p2;
idxB[idxJ]=p1;
}
else{
for(int a:listB[p2]){
listB[p1].add(a);
inc(map,(long)groupA[a]*(n+5)+p2,-1);
inc(map,(long)groupA[a]*(n+5)+p1,1);
groupB[a]=p1;
}
listB[p2].clear();
}
}
}
}
bw.flush();
}
static void inc(Map<Long,Integer> map,long a,int b){
map.put(a,map.getOrDefault(a,0)+b);
}
}
本题中利用线段树,需要维护的是每个叶子结点的相关信息,而且由于变化是线性的,因此可以生成一个转移矩阵,并为每个节点以一个转移矩阵最为懒标记,,,本题一开始略微卡常,因此在跟出题人沟通并得到帮助后,调大了时限,并修改了代码得以AC
import java.util.*;
import java.io.*;
public class Main{
static int mod=998244353,addMatrix[][][]=new int[7][7][],mulMatrix[][][]=new int[7][7][],sumMatrix[][]=new int[7][7];
static{
for(int i=0;i<7;i++){
for(int j=0;j<7;j++){
addMatrix[i][j]=new int[2];
mulMatrix[i][j]=new int[2];
}
addMatrix[i][i]=new int[]{1,0};
sumMatrix[i][i]=1;
mulMatrix[i][i]=new int[]{1,Math.max(0,5-i)};
for(int j=i+1;j<6;j++){
addMatrix[i][j]=new int[]{(int)comb(5-i,j-i),j-i};
}
}
for(int i=0;i<5;i++){
sumMatrix[6][i]=1;
}
}
public static void main(String args[]) throws IOException{
BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer sto=new StringTokenizer(bf.readLine());
int n=Integer.parseInt(sto.nextToken()),m=Integer.parseInt(sto.nextToken()),a[]=new int[n+5];
sto=new StringTokenizer(bf.readLine());
for(int i=1;i<=n;i++){
a[i]=Integer.parseInt(sto.nextToken());
}
SegTree st[]=new SegTree[n<<2|3];
st[1]=new SegTree(1,n);
build(st,a,1);
for(int i=0;i<m;i++){
sto=new StringTokenizer(bf.readLine());
int op=Integer.parseInt(sto.nextToken()),l=Integer.parseInt(sto.nextToken()),r=Integer.parseInt(sto.nextToken()),x=Integer.parseInt(sto.nextToken());
modify(st,1,l,r,generate(op==1?addMatrix:mulMatrix,x));
modify(st,1,1,n,sumMatrix);
}
for(int i=1;i<=n;i++){
bw.write(find(st,1,i)+" ");
}
bw.flush();
//System.out.println(st.val[6]);
}
static long find(SegTree st[],int idx,int a){
int l=st[idx].l,r=st[idx].r;
if(l==r){
return mulVec(st[idx].mat,st[idx].val)[6];
}
clear(st,idx);
return a<=(l+r)/2?find(st,idx<<1,a):find(st,idx<<1|1,a);
}
static void modify(SegTree st[],int idx,int a,int b,int mat[][]){
int l=st[idx].l,r=st[idx].r;
if(l==a&&r==b){
st[idx].pushed=false;
st[idx].mat=mulMatrix(mat,st[idx].mat);
}
else{
clear(st,idx);
int mid=(l+r)>>1;
if(b<=mid){
modify(st,idx<<1,a,b,mat);
}
else if(a>mid){
modify(st,idx<<1|1,a,b,mat);
}
else{
modify(st,idx<<1,a,mid,mat);
modify(st,idx<<1|1,mid+1,b,mat);
}
}
}
static void build(SegTree st[],int a[],int idx){
int l=st[idx].l,r=st[idx].r;
if(l<r){
int mid=(l+r)>>1;
st[idx<<1]=new SegTree(l,mid);
st[idx<<1|1]=new SegTree(mid+1,r);
build(st,a,idx<<1);
build(st,a,idx<<1|1);
}
else{
st[idx].val=new int[7];
for(int i=0;i<6;i++){
st[idx].val[i]=(int)pow(a[l],5-i);
}
}
}
static void clear(SegTree st[],int idx){
if(st[idx].l<st[idx].r&&!st[idx].pushed){
st[idx].pushed=true;
st[idx<<1].pushed=st[idx<<1|1].pushed=false;
st[idx<<1].mat=mulMatrix(st[idx].mat,st[idx<<1].mat);
st[idx<<1|1].mat=mulMatrix(st[idx].mat,st[idx<<1|1].mat);
for(int i=0;i<7;i++){
Arrays.fill(st[idx].mat[i],0);
st[idx].mat[i][i]=1;
}
}
}
static long pow(long a,long b){
long ans=1;
for(;b!=0;b>>=1,a=a*a%mod){
if(b%2==1){
ans=ans*a%mod;
}
}
return ans;
}
static long comb(long a,long b){
long ans=1;
for(int i=1;i<=b;i++){
ans=ans*(a-i+1)/i;
}
return ans;
}
static int[] mulVec(int a[][],int b[]){
int ans[]=new int[7];
for(int i=0;i<7;i++){
long s=0;
for(int j=0;j<7;j++){
s+=(long)a[i][j]*b[j];
}
ans[i]=(int)(s%mod);
}
return ans;
}
static int[][] mulMatrix(int a[][],int b[][]){
int ans[][]=new int[7][7];
for(int i=0;i<7;i++){
for(int j=0;j<7;j++){
long s=0;
for(int k=0;k<7;k++){
s+=(long)a[i][k]*b[k][j];
}
ans[i][j]=(int)(s%mod);
}
}
return ans;
}
static int[][] generate(int g[][][],long a){
int ans[][]=new int[7][7];
for(int i=0;i<7;i++){
for(int j=0;j<7;j++){
ans[i][j]=(int)(g[i][j][0]*pow(a,g[i][j][1])%mod);
}
}
return ans;
}
}
class SegTree{
int l,r;
int val[],mat[][]=new int[7][7];
boolean pushed=true;
SegTree(int l,int r){
this.l=l;
this.r=r;
for(int i=0;i<7;i++){
mat[i][i]=1;
}
}
}