星期天实在是太自闭了,各种状况各种错误,感觉自己的代码能力还是比较欠缺,而且队友的经验也相对比较不足,基本上一人卡题全队卡题。说到底还是自己的实力不够强,不能稳定把题给过掉。
最近要补的题实在是太多了,感觉还有很多很多要学的东西。实话说为什么这一届的水平比下一届差这么多,还是没有很努力去做事情。鸡汤多喝也无益,多多提升自己的水平才是最重要的。
A-串串
求问字符串里面有多少个本质不同的子串,是后缀自动机的一道模板题。
#include<bits/stdc++.h>
#define first F
#define second S
#define make_pair MP
typedef long long ll;
using namespace std;
const int N=1e5+50;
char s[N];
int len;
struct SAM{
int last,cnt,nxt[N*2][26],fa[N*2],l[N*2];
ll ans;
void init(){
last = cnt=1;
memset(nxt[1],0,sizeof nxt[1]);
fa[1]=0;ans=0;l[1]=0;
}
int inline newnode(){
++cnt;
memset(nxt[cnt],0,sizeof nxt[cnt]);
fa[cnt]=l[cnt]=0;
return cnt;
}
void add(int c){
int p = last;
int np = newnode();
last = np;
l[np] = l[p]+1;
while (p&&!nxt[p][c]){
nxt[p][c]=np;
p = fa[p];
}
if (!p){
fa[np]=1;
}else{
int q = nxt[p][c];
if (l[q]==l[p]+1){
fa[np] = q;
}else{
int nq = newnode();
memcpy(nxt[nq],nxt[q],sizeof nxt[q]);
fa[nq] = fa[q];
l[nq] = l[p]+1;
fa[np]=fa[q]=nq;
while (nxt[p][c]==q){
nxt[p][c]=nq;
p=fa[p];
}
}
}
ans+=l[last]-l[fa[last]];
}
void query(){
init();
for(int i=1;i<=len;i++){
add(s[i]-'a');
}
printf("%lld\n",ans);
}
}sam;
int main() {
scanf("%s",s+1);
len=strlen(s+1);
sam.query();
return 0;
}
C-二元
求问一个二元对里面选择k对,使得第一维的最小值和第二维的最小值之和最大。首先先考虑对第一维从大到小排序,然后枚举第一维,同时用一个优先队列维护第二维的最小值,更新答案。
#include<bits/stdc++.h>
#define first F
#define second S
#define make_pair MP
typedef long long ll;
using namespace std;
const int N=1e5+50;
struct node{
int x,y;
}a[N];
bool cmp(node a,node b) {
return a.x>b.x;
}
priority_queue< int,vector<int>,greater<int> > q;
int main() {
int n,k,sum=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++) {
if(i>=k+1) q.pop();
q.push(a[i].y);
if(i>=k) {
sum=max(sum,a[i].x+q.top());
}
}
printf("%d\n",sum);
return 0;
}
C-寻找
对于一棵树,每次给出三个点,求问ab路径上的哪一个点到c的距离是最小的。可以发现这个点一定是lca(a,c),lca(b,c),lca(a,b)当中的一个。这里还要判断一下lca(a,c),lca(b,c)是不是还在这个路径上面,然后直接去计算那个最小的就可以了。
#include<bits/stdc++.h>
#define first F
#define second S
#define make_pair MP
typedef long long ll;
using namespace std;
const int N=1e5+50;
int dep[N],Fa[N][22];
vector<int>G[N];
void dfs(int u,int fa) {
dep[u]=dep[fa]+1;
Fa[u][0]=fa;
for(int i=1;(1<<i)<=dep[u];i++) Fa[u][i]=Fa[Fa[u][i-1]][i-1];
for(auto &v:G[u]) {
if(v==fa) continue;
dfs(v,u);
}
}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--) if((1<<i)<=dep[x]-dep[y]) x=Fa[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--) if(Fa[x][i]!=Fa[y][i]) x=Fa[x][i],y=Fa[y][i];
return Fa[x][0];
}
int dis(int x,int y) { return dep[x]+dep[y]-2*dep[lca(x,y)]; }
int main() {
int n;
scanf("%d",&n);
for(int i=1;i<n;i++) {
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
int q;
scanf("%d",&q);
for(int i=1;i<=q;i++) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
int x=lca(a,c),y=lca(b,c),z=lca(a,b);
int vertex=z,ans=dis(z,c);
if(dis(x,a)+dis(x,b)==dis(a,b)) {
if(dis(x,c)<ans) {
vertex=x,ans=dis(x,c);
}
}
if(dis(y,a)+dis(y,b)==dis(a,b)) {
if(dis(y,c)<ans) {
vertex=y,ans=dis(y,c);
}
}
printf("%d\n",vertex);
}
return 0;
}
F-点对
给出的定义其实就是强连通分量,做一次tarjan缩点,然后对每个环统计一下有多少个点对。
#include<bits/stdc++.h>
#define first F
#define second S
#define make_pair MP
typedef long long ll;
using namespace std;
const int N=305;
vector<int>G[N];
int tim=0,dfn[N],low[N];
stack<int>s;
bool ins[N];
int ans=0;
void tarjan(int u) {
dfn[u]=low[u]=++tim;
s.push(u),ins[u]=true;
for(auto &v:G[u]) {
if(!dfn[v]) {
tarjan(v);
low[u]=min(low[v],low[v]);
}
else if(ins[v]) {
low[u]=min(low[u],low[v]);
}
}
if(dfn[u]==low[u]) {
int sum=1;
while(!s.empty()) {
int top=s.top();
if(s.top()!=u) { s.pop(),++sum,ins[top]=false; }
else break;
}
ans+=sum*(sum-1)/2;
}
}
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) {
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
printf("%d\n",ans);
return 0;
}
G-路径
给出一棵带权值树,然后要求最长的那个偶数边的链。偶数边的链可以认为是奇数点的链(不包含只有一个节点),dp[u][1]表示u节点出发向下的奇数个节点的链的最大值,dp[u][0]表示u节点出发向下偶数个节点的最大值。答案是对于每个节点出发的链和它的儿子出发的链连接起来。
#include<bits/stdc++.h>
#define first F
#define second S
#define make_pair MP
typedef long long ll;
using namespace std;
const int N=1e5+50;
struct edge{ int v,w; };
vector<edge> G[N];
ll dp[N][2];
ll ans=0; // 0代表偶数个结点 1代表奇数个结点
void dfs(int u,int fa) {
dp[u][0]=-1e18;
for(auto &e:G[u]) {
int v=e.v,w=e.w;
if(v==fa) continue;
dfs(v,u);
ans=max(ans,dp[u][0]+dp[v][1]+e.w);
ans=max(ans,dp[u][1]+dp[v][0]+e.w);
dp[u][0]=max(dp[u][0],dp[v][1]+e.w);
dp[u][1]=max(dp[u][1],dp[v][0]+e.w);
}
}
int main() {
int n;
scanf("%d",&n);
for(int i=1;i<n;i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G[u].push_back({v,w});
G[v].push_back({u,w});
}
dfs(1,0);
printf("%lld\n",ans);
return 0;
}
I-选择
直接用dp[i][0]表示没有选择这个位置,dp[i][1]表示选择了这个位置,然后转移这个dp就好了。
#include<bits/stdc++.h>
#define first F
#define second S
#define make_pair MP
typedef long long ll;
using namespace std;
const int N=105;
int a[N];
ll dp[N][2];
int main() {
int T;
scanf("%d",&T);
while(T--) {
int n;
scanf("%d",&n);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
dp[1][0]=0,dp[1][1]=a[1];
for(int i=2;i<=n;i++) {
dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
dp[i][1]=max(dp[i][1],dp[i-1][0]+a[i]);
}
printf("%lld\n",max(dp[n][0],dp[n][1]));
}
return 0;
}