【比赛报告】2018.8.8集训 NOIP练习赛卷三十四

本文是2018年集训NOIP练习赛的赛后报告,重点分析了三道题目:A题海星突击队构造问题,探讨了如何在保证安全的情况下最大化值班天数;B题K-斐波那契矩阵快速幂和扩展欧几里得的应用,介绍了在遇到此类问题时的解题思路;C题涉及graph构造、LCA和dfs序结合线段树的复杂问题。文章总结了这些题目对参赛者的挑战及解题策略。
摘要由CSDN通过智能技术生成

A.海星突击队 构造

题目链接

问题描述

海星突击队在一次执行任务中,有 n 个人在敌占区被围困,他们最后在一个隐蔽地
方安营扎寨,等待友军支援。
从安全角度出发,每天晚上他们必须安排人来值班,以防敌人发现袭击。他们的长
官决定每天安排至少 3 人值班才能保证基本安全。但是因为晚上值班过于无聊,值班的人
可能发生冲突,所以不能让任何 2 个人一起值班超过 3 次(不一定连续 3 次)。
请问突击队最多能度过多少天,并且保证没有任何人之间有冲突

输入

一个正整数 n ,表示有 n 个人

输出

第一行输出一个整数 ans ,表示最多可以安全度过 ans 天。
接下来 ans 行,每行若干个整数,表示这一天安排值班的人。如果有多组解,输出任意一
组即可。

【输入样例】
3
【输出样例】
3
1 2 3
1 2 3
1 2 3
对于 50% 的数据,保证 n<=99 。
对于 100% 的数据,保证 3<= n<=999 ,且 n 为奇数
本题有部分分数,安全度过的天数正确可以得 60%分数


比赛时草稿纸上把时间的规律推出来了(n-1)*n/2,,后来知道是C(n,2)
我们就假定能达到C(n,2)天,然后开始玄学构造(反正我自己没构造出来看了标程才知道)

#include<cstdio>
int main()
{
	//freopen("seal.in","r",stdin);
	//freopen("seal.out","w",stdout);
	int n,ans;
	scanf("%d",&n);
    ans=n*(n-1)/2;//感觉表不好打,直接在草稿纸上手动模拟,居然被我推出来了
	//结果就是C(n,2)... 
	printf("%d\n",ans);
	for(int i=1;i<=n>>1;i++)//枚举跨度 
	    for(int j=1;j<=n;j++)
	        printf("%d %d %d\n",j,(j+i-1)%n+1,(j+i+i-1)%n+1);//玄学构造 
	return 0;
}

B.K-斐波那契矩阵快速幂+扩展欧几里得

题目链接

这里写图片描述

【样例输入 】
5 5
【样例输出】
2
【样例解释】
f[0] = 2
f[1] = 2
f[2] = 4
f[3] = 6 mod 5 = 1
f[4] = 5 mod 5 = 0
f[5] = 1
30%的数据保证 n, P ≤ 1000
100%的数据保证 n, P ≤ 10^9


比赛时不知道怎么做,枚举了30分。后来才知道用矩阵快速幂和扩展欧几里得(本蒟蒻啥也不会,赶紧去学了一发)

#include<cstdio>
#include<cstring>
typedef long long ll;
int n,p;
struct Matrix{
	ll mx[2][2];
	friend Matrix operator *(Matrix a,Matrix b)
	{
		Matrix c;
		for(int i=0;i<2;i++)
		    for(int j=0;j<2;j++)
		    {
		    	c.mx[i][j]=0;
		    	for(int k=0;k<2;k++)
		    	    c.mx[i][j]=(c.mx[i][j]+a.mx[i][k]*b.mx[k][j])%p;
		    }
		return c;
	}
	friend Matrix operator ^(Matrix a,int num)
	{
		Matrix c;
		memset(c.mx,0,sizeof(c.mx));
		for(int i=0;i<2;i++)
		    c.mx[i][i]=1;
		while(num)
		{
			if(num&1)c=c*a;
			num>>=1;
			a=a*a;
		}
		return c;    
	}
};
bool exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1;y=0;return a==1;
	}
	else if(!exgcd(b,a%b,y,x))return false;
	y-=a/b*x;return true;
}
int main()
{
	scanf("%d%d",&n,&p);
	Matrix base;
	base.mx[0][0]=1;
	base.mx[0][1]=1;
	base.mx[1][0]=1;
	base.mx[1][1]=0;	
	Matrix ans;
	ans=base^(n-1);//写成base^n,WA一万年 
	int a=(ans.mx[0][0]+ans.mx[1][0])%p,x,y;
	if(exgcd(a,p,x,y))
		printf("%d\n",(x+p)%p);
	else puts("None");
	return 0;
}

C.graph 构造+LCA+dfs序+线段树

题目链接
在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 200000 + 10;
const long long inf = 1ll<<60;
const int maxh = 20;
int n, q, a[N*2], b[N*2], c[N*2], rev[N], st[N], ed[N], idfn, head[N], cnt;
long long bit[N], dist[N];
struct Edge {
    int nxt, to, val;
} e[N * 2];
void addedge(int u, int v, int w) {
    e[++cnt].nxt = head[u];
    e[cnt].to = v;
    e[cnt].val = w;
    head[u] = cnt;
}

// Segment Tree 
#define lson l , m , rt << 1  
#define rson m + 1 , r , rt << 1 | 1 
const int MAXN = 200000 + 10;
long long add[MAXN<<2], mn[MAXN<<2];
void PushUp(int rt) {   mn[rt] = min(mn[rt<<1], mn[rt<<1|1]);   }
void PushDown(int rt, int m) {
    if(add[rt]) {
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        mn[rt<<1] += add[rt];
        mn[rt<<1|1] += add[rt];
        add[rt] = 0;
    }
}
void build(int l, int r, int rt) {
    add[rt] = 0;
    if(l == r) {
        mn[rt] = dist[l];
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
void update(int L, int R, long long w, int l, int r, int rt) {
    if(L>R) return;
    if(L <= l && r <= R) {
        add[rt] += w;
        mn[rt] += w;
        return;
    }
    PushDown(rt, r-l+1);
    int m = (l+r) >> 1;
    if(L <= m)  update(L, R, w, lson);
    if(m < R)   update(L, R, w, rson);
    PushUp(rt);
}
long long query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R)    return mn[rt];
    PushDown(rt, r-l+1);
    int m = (l+r) >> 1;
    long long ret = inf;
    if(L <= m)  ret = min(ret, query(L, R, lson));
    if(m < R)   ret = min(ret, query(L, R, rson));
    return ret;
}

// LCA 
int dep[N];
int anc[N][20];
void dfs(int rt) {
    static int Stack[N];
    int top = 0;
    dep[rt] = 1;
    for(int i=0;i<maxh;i++)
        anc[rt][i] = rt;
    Stack[++top] = rt;
    while(top) {
        int x = Stack[top];
        if(x != rt) {
            for(int i=1, y;i<maxh;i++)
                y = anc[x][i-1],    anc[x][i] = anc[y][i-1];
        }
        for(int &i=head[x];i;i=e[i].nxt) {
            int y = e[i].to;
            if(y != anc[x][0]) {
                dep[y] = dep[x] + 1;
                anc[y][0] = x;
                Stack[++top] = y;
            }
        }
        while(top && head[Stack[top]] == 0) top--;
    }
}
void swim(int &x, int H) {
    for(int i=0;H;i++) {
        if(H & 1)   x = anc[x][i];
        H /= 2;
    }
}
int lca(int x, int y) { 
    int i;
    if(dep[x] > dep[y]) swap(x, y);
    swim(y, dep[y]-dep[x]);
    if(x == y)  return x;
    for(;;) {
        for(i=0;anc[x][i] != anc[y][i];i++);
        if(i == 0)  return anc[x][0];
        x = anc[x][i-1];
        y = anc[y][i-1];
    }
    return -1;
}
void getDfn(int rt, int fa, long long w) 
{
    st[rt] = ++idfn;
    dist[st[rt]] = w + rev[rt];
    for(int i=head[rt];i;i=e[i].nxt) {
        if(e[i].to == fa)   continue;
        getDfn(e[i].to, rt, w+e[i].val);
    }
    ed[rt] = idfn;
}
int main(){	
    //freopen("in.txt","r",stdin); 
    scanf("%d %d", &n, &q);
    for(int i=1;i<=2*n-2;i++)   {
        scanf("%d %d %d", &a[i], &b[i], &c[i]);
        if(i < n)   addedge(a[i], b[i], c[i]),  addedge(b[i], a[i], c[i]);
        else    rev[ a[i] ] = c[i];
    }

    getDfn(1, 0, 0);
    dfs(1);
    build(1, n, 1);

    for(int op, i, w, u, v;q--;)   {
        scanf("%d", &op);
        if(op == 1) {
            scanf("%d %d", &i, &w);
            if(i >= n) {
                update(st[ a[i] ], st[ a[i] ], w-rev[ a[i] ], 1, n, 1);
                rev[ a[i] ] = w;
            } else {
                update(st[ b[i] ], ed[ b[i] ], w-c[i], 1, n, 1);
                c[i] = w;
            }
        } else {
            scanf("%d %d", &u, &v);
            if(lca(u, v) == u) {
                long long du = query(st[u], st[u], 1, n, 1) - rev[u];
                long long dv = query(st[v], st[v], 1, n, 1) - rev[v];
                printf("%lld\n", dv - du);
            } else {
                long long mnDist = query(st[u], ed[u], 1, n, 1) - query(st[u], st[u], 1, n, 1) + rev[u];
                long long dv = query(st[v], st[v], 1, n, 1) - rev[v];
                printf("%lld\n", mnDist + dv);
            }
        }
    }
}

总结

难度挺大的一题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值