2022 纪中集训 7.6
T1
传递闭包 + spfa
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL N = 105, M = 10005;
LL n,m,k;
LL g[N][N];
LL dis[N][N];
LL f[N][N];
queue<LL> q;
void spfa ()
{
memset(f,0x3f,sizeof f);
q.push(1);
f[1][0]=0;
while (!q.empty())
{
LL u=q.front();
q.pop();
for (LL i=1; i<=n; i++)
if (g[u][i]!=INF)
{
for (LL j=0; j<=k; j++)
if (dis[i][u]!=INF) // 不能互达
{
if (f[i][j] > f[u][j]+g[u][i]) f[i][j] = f[u][j] + g[u][i], q.push(i);
}
else
{
if (f[i][j] > f[u][j-1]+g[u][i]) f[i][j] = f[u][j-1] + g[u][i], q.push(i);
}
}
}
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
memset(g,0x3f,sizeof g);
memset(dis,0x3f,sizeof dis);
for(LL i=1; i<=m; i++)
{
LL u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
dis[u][v] = g[u][v] = min(g[u][v], w);
}
for(LL i=1; i<=n; i++)
for(LL j=1; j<=n; j++)
for(LL k=1; k<=n; k++)
if(i != j && j != k && i != k && dis[i][j] > dis[i][k] + dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
for(LL i=1; i<=n; i++)
for(LL j=1; j<=n; j++)
if(dis[j][i] == INF && g[i][j] != INF)
g[i][j] <<= 1;
spfa();
LL ans = INF;
for(LL i=1; i<=k; i++) ans = min(ans, f[n][i]);
if(ans == INF) cout<<-1;
else cout<<ans;
return 0;
}
T2
二分 + 字符串哈希
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N = 50005;
int n, m;
char a[N],b[N];
ULL p = 131, power[N];
ULL hash1[N], hash2[N];
int main()
{
scanf("%s%s",a+1,b+1);
n = strlen(a+1), m = strlen(b+1);
power[0] = 1;
for(int i=1;i<=max(n, m);i++) power[i] = power[i-1] * p;
for(int i=1;i<=n;i++) hash1[i] = hash1[i-1] * p + a[i] - 'a' + 1;
for(int i=1;i<=m;i++) hash2[i] = hash2[i-1] * p + b[i] - 'a' + 1;
int ans = 0;
for(int i=1;i<=m;i++)
{
int l = 0, r = min(n, m - i + 1);
while(l <= r)
{
int mid = l + r >> 1;
if(hash1[mid] == hash2[i+mid-1] - hash2[i-1] * power[mid]) l = mid + 1;
else r = mid - 1;
}
int len = l - 1;
l = 0, r = min(n - len - 1, m - len - i);
while(l <= r)
{
int mid = l + r >> 1;
if(hash1[len + 1 + mid] - hash1[len + 1] * power[mid] == hash2[i+mid+len] - hash2[i+len] * power[mid]) l = mid + 1;
else r = mid - 1;
}
ans = max(ans, len + l);
}
cout<<min(ans, n);
return 0;
}
T3
先考虑序列上面,求最长不下降子序列的方法,
其中就有一种方法就是用线段树。
于是就将这种方法扩展到树上,而且子树根节点是必须选到的。
因为每个点的权值都在n以内,
所以线段树上面的每一个叶子节点就表示当最后一个位置的值为x的时候最长可以有多少。
那么新加入一个就是在原有线段树上面,查询小于等于它的全部的最长一个,
然后当前这个,也就是根节点,必须选的。
如果每个节点只有只有一个儿子,那么就很方便了。
如果有多个儿子,也就是线段树合并。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 100005;
struct node
{
int l,r,mx;
}tr[N*40];
int n,m,nxt[N],to[N],lst[N],x,y,tot;
int opl,opr,op,v[N],ans[N];
void ins(int x,int y)
{
nxt[++tot]=lst[x];
to[tot]=y;
lst[x]=tot;
}
void mix(int x,int y,int l,int r)
{
if(l==r)
{
tr[x].mx=max(tr[x].mx,tr[y].mx);
return;
}
int m=(l+r)>>1;
if(tr[y].l)
if(!tr[x].l)tr[x].l=tr[y].l;else mix(tr[x].l,tr[y].l,l,m);
if(tr[y].r)
if(!tr[x].r)tr[x].r=tr[y].r;else mix(tr[x].r,tr[y].r,m+1,r);
tr[x].mx=max(tr[tr[x].l].mx,tr[tr[x].r].mx);
}
void find(int x,int l,int r)
{
if(!x)return;
if(opl<=l && r<=opr)
{
op=max(op,tr[x].mx);
return;
}
int m=(l+r)>>1;
if(opl<=m)find(tr[x].l,l,m);
if(m<opr)find(tr[x].r,m+1,r);
}
void work(int x,int l,int r)
{
if(opl<=l && r<=opr)
{
tr[x].mx=max(tr[x].mx,op);
return;
}
int mid=(l+r)>>1;
if(opl<=mid)
{
if(!tr[x].l)tr[x].l=++m;
work(tr[x].l,l,mid);
}
if(mid<opr)
{
if(!tr[x].r)tr[x].r=++m;
work(tr[x].r,mid+1,r);
}
tr[x].mx=max(tr[tr[x].l].mx,tr[tr[x].r].mx);
}
void dfs(int x)
{
for(int i=lst[x];i;i=nxt[i])
{
dfs(to[i]);
mix(x,to[i],1,n);
}
op=0;opl=1;opr=v[x];
find(x,1,n);
opl=opr=v[x];
ans[x]=op=op+1;
work(x,1,n);
}
int main()
{
scanf("%d%d",&n,&x);
m=n;
for(int i=2;i<=n;i++) scanf("%d",&x),ins(x,i);
for(int i=1;i<=n;i++) scanf("%d",&v[i]);
dfs(1);
for(int i=1;i<=n;i++) printf("%d ", ans[i]);
return 0;
}
T4
Nim 游戏结论, 如果所有堆的异或和为0,则先手必败。
只需要取走p个石子,使得异或和为0。
#include<cstdio>
using namespace std;
#define N 100005
int s[N];
int main()
{
int n,m,i,a,b,k,sum,ans;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",s+i);
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d",&k,&a,&b);
if(b==k)
{
if(s[k]) printf("%d\n",s[k]),s[k]=0;
else puts("-1");continue;
}
for(i=k-1,sum=0;i>b;i--) sum^=s[i];
for(ans=0;i>=a;i--)
{
sum^=s[i];
if(ans<s[k]-sum) ans=s[k]-sum;
if(ans==s[k]) break;
}
if(ans) s[k]-=ans,printf("%d\n",ans);
else puts("-1");
}
return 0;
}