2022 纪中集训 7.6

T1

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

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

image

先考虑序列上面,求最长不下降子序列的方法,
其中就有一种方法就是用线段树。
于是就将这种方法扩展到树上,而且子树根节点是必须选到的。
因为每个点的权值都在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

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;
}
posted @ 2022-07-06 15:59  BorisDimitri  阅读(25)  评论(0编辑  收藏  举报