首页 > 其他分享 >0112总结

0112总结

时间:2023-01-12 19:47:27浏览次数:41  
标签:0112 总结 return fa int 矩阵 ++ np

四道题都比较套路,AK了。

T1 [模拟赛20230112] 密接

枚举区间的左端点,再枚举众数出现的次数,那么满足条件的右端点就是一段区间。令 \(pos1_i\) 为第一个出现 \(i\) 次的数的位置,\(pos2_i\) 位第二个。那么这段区间就是 \([pos1_i,min{pos2_i,pos1_i+1})\)。
然后考虑维护 \(pos\)。从右往左枚举左端点,在左端点新加进来一个数,考虑这个数在之后出现的位置,可以有可能将 \(pos\) 变小,就直接更新。
复杂度 \(O(100n)\)。

#include<bits/stdc++.h>
using namespace std;
inline int in(){
	int x;
	scanf("%d",&x);
	return x;
}
const int N=1e5+5;
int n,m,a[N],b[N];
int mn1[105],mn2[105];
int id[N],pos[N][105],cnt[N];
int main(){
	// freopen("close.in","r",stdin);
	// freopen("close.out","w",stdout);
	n=in();
	for(int i=1;i<=n;i++)a[i]=b[i]=in();
	sort(b+1,b+n+1),m=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(b+1,b+m+1,a[i])-b;
		id[i]=++cnt[a[i]];
		pos[a[i]][cnt[a[i]]]=i;
	}
	for(int i=1;i<=101;i++)mn1[i]=mn2[i]=n+1;
	long long ans=0;
	for(int i=n;i>=1;i--){
		int p=id[i];
		for(int x=p,y=1;x<=cnt[a[i]];x++,y++){
			int v=pos[a[i]][x];
			if(v<mn1[y])mn2[y]=mn1[y],mn1[y]=v;
			else if(v<mn2[y])mn2[y]=v;
		}
		for(int j=1;j<=100;j++){
			ans+=min(mn1[j+1],mn2[j])-mn1[j];
		}
	}
	cout<<ans<<endl;
	return 0;
}

T2 [模拟赛20230112] 确诊

没什么好说的,直接单调队列维护sg转移就行。(好像这个题也可以直接贪心)

#include<bits/stdc++.h>
using namespace std;
const int N=5e6+5;
int n,k;
int pri[N],inp[N],pc;
int f[N];
int q1[N],l1,r1,q2[N],l2,r2;
int q3[N],l3,r3,q4[N],l4,r4;
int main(){
	// freopen("game.in","r",stdin);
	// freopen("game.out","w",stdout);
	cin>>n>>k;
	for(int i=2;i<=n;i++){
		if(!inp[i])pri[++pc]=i;
		for(int j=1;j<=pc&&i*pri[j]<=n;j++){
			inp[i*pri[j]]=1;
			if(i%pri[j]==0)break;
		}
	}
	l1=l2=l3=l4=1;
	for(int i=2;i<n;i++){
		while(l1<=r1&&i-k>q1[l1])l1++;
		while(l2<=r2&&i-k>q2[l2])l2++;
		while(l3<=r3&&i-k>q3[l3])l3++;
		while(l4<=r4&&i-k>q4[l4])l4++;
		if(inp[i]){
			if(l2<=r2)f[i]=-f[q2[l2]]+1;
			else if(l1<=r1)f[i]=-f[q1[l1]]-1;
			if(f[i]>0){
				while(l3<=r3&&f[q3[r3]]<=f[i])r3--;
				q3[++r3]=i;
			}else{
				while(l4<=r4&&f[q4[r4]]<=f[i])r4--;
				q4[++r4]=i;
			}
		}else{
			if(l4<=r4)f[i]=-f[q4[l4]]+1;
			else if(l3<=r3)f[i]=-f[q3[l3]]-1;
			if(f[i]>0){
				while(l1<=r1&&f[q1[r1]]<=f[i])r1--;
				q1[++r1]=i;
			}else{
				while(l2<=r2&&f[q2[r2]]<=f[i])r2--;
				q2[++r2]=i;
			}
		}
	}
	int ans=1e9;
	for(int i=max(1,n-k);i<n;i++)if(!inp[i]){
		if(ans==1e9)ans=f[i];
		if(f[i]<=0&&ans>0)ans=f[i];
		if(f[i]<=0||ans>0)ans=max(ans,f[i]);
	}
	if(ans==1e9)return cout<<0,0;
	ans=-ans;if(ans>=0)ans++;else ans--;
	cout<<ans<<endl;
	return 0;
}

T3 [模拟赛20230112] 重症

比较套路的矩阵树板子题。
众所周知,矩阵树的边权不仅可以是一个数,还能是一个多项式。
但是我比较愚蠢,没有构造出多项式,不过这个东西是好用矩阵进行转移的,于是就突发奇想:能不能行列式套矩阵?
于是就试了试,具体地,要写矩阵加减乘以及求逆。但是就遇到了一个问题:矩阵乘法不满足交换律,那么求行列式时就不能进行行变换!
但是最后硬着头皮写完,居然AC了!
究其根本,虽然矩阵乘法没有交换律,但是这种矩阵树的题,矩阵对应的是每条边的边权,而对矩阵乘法的顺序进行交换,就相当于对树的dfs序的顺序进行交换,这显然是不会影响到最终的答案的,所以在矩阵树上套矩阵然后直接求行列式是正确的。
感觉矩阵树套矩阵可以解决很多问题,但是常数比直接套多项式大得多。

#include<bits/stdc++.h>
using namespace std;
inline int in(){
	int x;
	scanf("%d",&x);
	return x;
}
const int N=205,mod=998244353;
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 qpow(int a,int b){
	int c=1;
	for(;b;b>>=1,a=mul(a,a))if(b&1)c=mul(c,a);
	return c;
}
struct mtx{
	int a[4][4];
	int* operator [](int x){return a[x];}
	mtx(){
		for(int i=0;i<4;i++)
			for(int j=0;j<4;j++)
				a[i][j]=0;
	}
};
void print(mtx a){
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++)cout<<a[i][j]<<' ';
		cout<<endl;
	}
	cout<<endl;
}
mtx I(){
	mtx a;
	for(int i=0;i<4;i++)a[i][i]=1;
	return a;
}
mtx build(int c,int d){
	mtx a=I();
	a[0][1]=c,a[0][2]=d,a[0][3]=mul(c,d);
	a[1][3]=d,a[2][3]=c;
	return a;
}
mtx operator + (mtx a,mtx b){
	mtx c;
	for(int i=0;i<4;i++)
		for(int j=0;j<4;j++)
			c[i][j]=add(a[i][j],b[i][j]);
	return c;
}
mtx operator - (mtx a,mtx b){
	mtx c;
	for(int i=0;i<4;i++)
		for(int j=0;j<4;j++)
			c[i][j]=add(a[i][j],mod-b[i][j]);
	return c;
}
mtx operator * (mtx a,mtx b){
	mtx c;
	for(int i=0;i<4;i++)
		for(int k=0;k<4;k++)
			for(int j=0;j<4;j++)	
				c[i][j]=add(c[i][j],mul(a[i][k],b[k][j]));
	return c;
}
mtx getinv(mtx a){
	static int b[4][8];
	for(int i=0;i<4;i++)
		for(int j=0;j<8;j++){
			if(j<4)b[i][j]=a[i][j];
			else b[i][j]=(j==i+4);
		}
	for(int i=0;i<4;i++){
		int inv=qpow(b[i][i],mod-2);
		for(int j=i;j<8;j++)b[i][j]=mul(b[i][j],inv);
		for(int j=i+1;j<4;j++){
			for(int k=j+1;k<8;k++)
				b[j][k]=add(b[j][k],mod-mul(b[i][k],b[j][i]));
		}
	}
	for(int i=3;i>=0;i--){
		for(int j=i-1;j>=0;j--){
			int v=b[j][i];
			for(int k=i;k<8;k++)
				b[j][k]=add(b[j][k],mod-mul(v,b[i][k]));
		}
	}
	for(int i=0;i<4;i++)
		for(int j=0;j<4;j++)
			a[i][j]=b[i][j+4];
	return a;
}
int n,m;
mtx f[N][N];
mtx det(){
	mtx res=I();
	for(int i=1;i<n;i++){
		res=res*f[i][i];
		mtx inv=getinv(f[i][i]);
		for(int j=i;j<n;j++)f[i][j]=f[i][j]*inv;
		for(int j=i+1;j<n;j++){
			for(int k=n-1;k>=i;k--)
				f[j][k]=f[j][k]-(f[i][k]*f[j][i]);
		}
	}
	return res;
}
int main(){
	// freopen("icu.in","r",stdin);
	// freopen("icu.out","w",stdout);
	n=in(),m=in();
	while(m--){
		int x=in(),y=in(),c=in(),d=in();
		mtx a=build(c,d);
		f[x][x]=f[x][x]+a,f[y][y]=f[y][y]+a;
		f[x][y]=f[x][y]-a,f[y][x]=f[y][x]-a;
	}
	mtx res=det();
	cout<<res[0][3]<<endl;
	return 0;
}

T4 [模拟赛20230112] 康复

比较套路的字符串题。
要字典序大于 \(X\),又要字典序最小,那么结果一定是 \(X\) 的一个前缀再加上一个字符。
于是枚举这个前缀 \(X_1 \sim X_i\),找出 \([l,r]\) 区间内这个前缀的出现位置,看一看有没有接下来一个字符大于 \(X_{i+1}\) 的,然后要接下来一个字符最小的。
具体地,就直接在 SAM 的 parent 树上对 26 个字符分别用线段树维护其末尾位置,然后查询的时候枚举字符再区间查询。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=1e6+5;
int n,q,ql[N],qr[N],qk[N];
char s[N],ss[N],*t[N];
int *pos[N];
int fa[M],len[M],ch[M][26],lst=1,tot=1,tag[M];
void insert(int c){
	int p=lst,np=++tot;lst=np;
	len[np]=len[p]+1;
	for(;!ch[p][c];p=fa[p])ch[p][c]=np;
	if(!p){fa[np]=1;return;}
	int q=ch[p][c];
	if(len[q]==len[p]+1){fa[np]=q;return;}
	int nq=++tot;len[nq]=len[p]+1;
	fa[nq]=fa[q],fa[q]=fa[np]=nq;
	for(int i=0;i<26;i++)ch[nq][i]=ch[q][i];
	for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
vector<int> e[M];
int rt[M][26],tot1;
struct node{
	int ls,rs;
}T[M*25];
#define ls(x) T[(x)].ls
#define rs(x) T[(x)].rs
void insert(int &p,int l,int r,int d){
	if(!p)p=++tot1;
	if(l==r)return;
	int mid=l+r>>1;
	if(d<=mid)insert(ls(p),l,mid,d);
	else insert(rs(p),mid+1,r,d);
}
bool query(int p,int l,int r,int ql,int qr){
	if(!p)return 0;
	if(ql<=l&&r<=qr)return 1;
	int mid=l+r>>1;bool res=0;
	if(ql<=mid)res=query(ls(p),l,mid,ql,qr);
	if(mid<qr&&!res)res=query(rs(p),mid+1,r,ql,qr);
	return res;
}
int merge(int p,int q,int l,int r){
	if(!p||!q)return p|q;
	if(l==r)return p;
	int mid=l+r>>1;
	int x=++tot1;
	ls(x)=merge(ls(p),ls(q),l,mid);
	rs(x)=merge(rs(p),rs(q),mid+1,r);
	return x;
}
void dfs(int x){
	if(tag[x]||x==1){
		int c=s[tag[x]+1]-'a';
		insert(rt[x][c],0,n-1,tag[x]);
	}
	for(int y:e[x]){
		dfs(y);
		for(int i=0;i<26;i++)
			rt[x][i]=merge(rt[x][i],rt[y][i],0,n-1);
	}
}
int main(){
	// freopen("heal.in","r",stdin);
	// freopen("heal.out","w",stdout);
	scanf("%s",s+1),n=strlen(s+1);
	for(int i=1;i<n;i++){
		insert(s[i]-'a');
		while(len[fa[lst]]==len[lst])lst=fa[lst];
		tag[lst]=i;
	}
	tag[1]=0;
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		scanf("%d %d %s",ql+i,qr+i,ss+1);
		qk[i]=strlen(ss+1);
		t[i]=new char [qk[i]+3];
		pos[i]=new int [qk[i]+2];
		for(int j=0;j<qk[i]+2;j++)t[i][j]=ss[j];
		lst=1;
		for(int j=1;j<=qk[i];j++){
			insert(ss[j]-'a');
			while(len[fa[lst]]==len[lst])lst=fa[lst];
			pos[i][j]=lst;
		}
		pos[i][0]=1;
	}
	for(int i=2;i<=tot;i++)e[fa[i]].push_back(i);
	dfs(1);
	for(int i=1;i<=q;i++){
		int l=ql[i],r=qr[i],k=qk[i];
		bool flag=0;
		for(int j=min(r-l,k);j>=0;j--){
			int c=j==k?0:t[i][j+1]-'a'+1,x=pos[i][j];
			for(int y=c;y<26;y++){
				if(query(rt[x][y],0,n-1,l+j-1,r-1)){
					t[i][j+1]=y+'a',t[i][j+2]=0;
					flag=1;break;
				}
			}
			if(flag)break;
		}
		if(!flag)puts("-1");
		else printf("%s\n",t[i]+1);
	}
	return 0;
}

标签:0112,总结,return,fa,int,矩阵,++,np
From: https://www.cnblogs.com/william555/p/17047743.html

相关文章

  • 工作中需知道的数组方法总结
    数组遍历操作forEach该方法等同于for循环,其没有返回值结构:arr.forEach(回调函数,回调函数this的值)第二个参数当回调函数是箭头函数时无效用法:arr.forEach(function(item,......
  • 2022 年度总结
    前言写这篇博客的起因是,公司要求写年度个人总结。我翻了下网上的总结,都不太符合自己的情况,于是打算真情实感地原创一篇。年度总结,无非就是梳理下有没有完成年初的flag,再......
  • 1月12日内容总结——文件和文件索引、链接、系统时间、克隆、定时任务、paramiko模块
    目录一、文件相关信息二、文件索引信息三、链接信息四、系统时间五、机器克隆六、定时任务七、paramiko模块八、公钥私钥九、paramiko其他操作十、代码封装十一、面试题回......
  • 简单图论总结
    一些碎碎念最近复习了一些简单图论的知识,主要有floyd,spfa,拓扑排序,dij,差分等等。其实真正意义上的算法就是floyd和dij以及拓扑排序(spfa目前的应用场景仅限于有负边的最短路......
  • sc stream-rabbit 简化版20230112
     二、消费者【2058】    1、pom.xml        <dependency>            <groupId>org.springframework.cloud</groupId>    ......
  • JPQL语法总结对比原生sql
    JPQL语法总结对比原生sql原文连接:https://blog.csdn.net/u013321164/article/details/17675617JPQL主要用于JPA查询数据,和SQL语句的语法大同小异; 最基本的查询:S......
  • elemnet-plus 使用总结
    1.el-date-picker设置起始周的日期备注:如果添加dayjs.en.weekStart=2不起作用需要检查是否添加了el-config-provider语言设置或者在app.vue中添加<template><......
  • 网络流模板及易错点总结
    网络流模板及易错点总结一、最大流#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintNN=300,MM=5e3+8,INF=0x7f7f7f7f;intn,m,......
  • Vue 中 Promise 的then方法异步使用及async/await 异步使用总结
    转载请注明出处:1.Promise的then方法使用then方法是 Promise中处理的是异步调用,异步调用是非阻塞式的,在调用的时候并不知道它什么时候结束,也就不会等到他......
  • 年终总结
    不知不觉又到年底了,马上要过年了,特此在今夜做一篇年终总结,来回顾今年历经之感悟。我一直在想,从月薪三千走到过万需要多久,刚来南京的时候,我头一次站在中华门外(第一家公司......