noip2019集训测试赛(四)

Problem A: fibonacci

Time Limit: 3000 ms Memory Limit: 256 MB

Description

Input

第一行两个数 N,M .

第二行 N 个数 a1,a2,...,an .

接下来 M 行, 每行代表题目描述中的一种操作.

Output

对于每个询问, 输出一行, 表示答案.

Sample Input
5 4
1 1 2 1 1
2 1 5
1 2 4 2
2 2 4
2 1 5
Sample Output
5
7
9

HINT

对于 30% 的数据, 1≤N,M≤50 .

对于 60% 的数据, 1≤x,ai≤10^5 .

对于 100% 的数据, 1≤N,M≤10^5 , 1≤x,ai≤10^9 .

Solution

我们发现区间的斐波那契数列和也满足单个斐波那契数列的性质

所以我们可以用线段树维护矩阵快速幂

每次修改前一定要先处理好要修改的矩阵,不能pushdown的时候才求出来(否则复杂度是两只log的,会TLE70)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
struct node{
    int a,b,c,d;
    node(int a=1,int b=0,int c=0,int d=1):a(a),b(b),c(c),d(d){}
}tag[400001];
node operator *(node a,node b){
    //cout<<(a.a*b.a+a.b*b.c)%mod<<endl;
    return {(a.a*b.a+a.b*b.c)%mod,(a.a*b.b+a.b*b.d)%mod,(a.c*b.a+a.d*b.c)%mod,(a.c*b.b+a.d*b.d)%mod};
}
void fib(int x,int &a,int &c){
    x--;
    node tmp(1,1,1,0),ans;
    while(x){
        if(x&1)ans=ans*tmp;
        //cout<<ans.a<<" "<<ans.c<<endl;
        tmp=tmp*tmp;
        x>>=1;
    }
    a=ans.a,c=ans.c;
}
struct nd{
    int a,c;
}t[400001];
void add(node tg,int &a,int &c){
    node m=node(a,0,c,0);
    m=tg*m;
    a=m.a,c=m.c;
}
void pushdown(int o){
    tag[o*2]=tag[o]*tag[o*2];
    tag[o*2+1]=tag[o]*tag[o*2+1];
    add(tag[o],t[o*2].a,t[o*2].c);
    add(tag[o],t[o*2+1].a,t[o*2+1].c);
    tag[o]=node();
}
int a[400001];
void build(int o,int l,int r){
    if(l==r){
        fib(a[l],t[o].a,t[o].c);
        return;
    }
    int mid=(l+r)/2;
    build(o*2,l,mid);
    build(o*2+1,mid+1,r);
    t[o].a=(t[o*2].a+t[o*2+1].a)%mod;
    t[o].c=(t[o*2].c+t[o*2+1].c)%mod;
}
void update(int o,int l,int r,node f,int L,int R){
    if(L<=l&&r<=R){
        tag[o]=f*tag[o];
        add(f,t[o].a,t[o].c);
        return;
    }
    pushdown(o);
    int mid=(l+r)/2;
    if(L<=mid)update(o*2,l,mid,f,L,R);
    if(R>mid)update(o*2+1,mid+1,r,f,L,R);
    t[o].a=(t[o*2].a+t[o*2+1].a)%mod;
    t[o].c=(t[o*2].c+t[o*2+1].c)%mod;
}
int query(int o,int l,int r,int L,int R){
    if(L<=l&&r<=R)return t[o].a;
    pushdown(o);
    int mid=(l+r)/2,ret=0;
    if(L<=mid)ret=(ret+query(o*2,l,mid,L,R))%mod;
    if(R>mid)ret=(ret+query(o*2+1,mid+1,r,L,R))%mod;
    return ret;
}
signed main(){
    int n,m;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
    }
    build(1,1,n);
    for(int i=1;i<=m;++i){
        int opt,l,r;
        scanf("%lld%lld%lld",&opt,&l,&r);
        if(opt==1){
            int x;
            scanf("%lld",&x);
            int a,c;
            fib(x,a,c);
            update(1,1,n,node((a+c)%mod,a,a,c),l,r);
        }
        else {
            printf("%lld\n",query(1,1,n,l,r));
        }
    }
}

Problem B: game

Time Limit: 1000 ms Memory Limit: 256 MB

Description

给定一棵 n 个节点, 以 1 为根的有根树.

每个点 i 有黑白两种颜色中的一种, 记作 coli .

Alice 和 Bob 在树上玩游戏.

他们轮流进行操作, Alice 先手.

对于每次操作, 当前操作方可以从树上某个白色的点开始, 将该点到根的路径上经过的所有点都涂黑.

谁无法操作谁输.

问 Alice 是否有必胜策略. 如果没有, 输出 ?1 , 否则按照从小到大的顺序输出可能选择的第一个节点.

Input

第一行一个整数 n .

第二行 n 个整数 col1,col2,...,coln . 且 coli 只可能等于 0 或 1 , 0 表示白色, 1 表示黑色,

接下来 n?1 行, 每行两个数 u,v , 表示 (u,v) 是这棵有根树的一条树边.

Output

如果没有必胜策略, 输出 ?1 .

如果有必胜策略, 按照从小到大的顺序输出可能选择的第一个节点.

Sample Input
8
1 1 0 1 0 0 1 0
1 2
1 3
2 6
3 4
3 5
5 7
7 8
Sample Output
5

HINT

对于 50% 的数据, n≤1000 .

对于 100% 的数据, n≤100000 .

Solution

不会博弈论。

等我学了再说。

Problem C: graph

Time Limit: 3000 ms Memory Limit: 128 MB

Description

输入文件: graph.in

输出文件: graph.out

给定一张 n 个点 m 条边的无向图, 问删去每个点后, 原图是不是二分图.

Input

输入文件包含多组数据, 文件开头给定数据组数 T .

对于每组数据:

第一行两个整数 n,m .

接下来 m 行, 每行两个整数 u,v , 表示图中有边 (u,v) , 保证 u≠v .

Output

对于每组数据, 输出一行长度为 n 的字符串 s , si=0 表示删除第 i 个点后原图不是二分图, si=1 表示删除后是二分图.

Sample Input
2
5 4
1 4
2 4
3 5
4 5
5 5
1 2
1 3
2 3
2 4
3 5
Sample Output
11111
11100

HINT

对于 20% 的数据, 1≤n,m≤1000

对于 60% 的数据, 数据近似随机.

对于 100% 的数据, 1≤t≤5 , 1≤n,m≤105 , 1≤u,v≤n, u≠v

Solution

考虑分治,用按秩合并的并查集。

二分删去的点,比如假如要删去的点是l~mid,我们就把mid+1~r的所有边加入并查集,然后merge的时候如果两个点有同一个父亲而且颜色相同,就构成了奇环,就不是二分图了

如果不用按秩合并会T60分

每次处理完一个区间以后要还原。

#include<bits/stdc++.h>
using namespace std;
struct node{
    int u,v;
    int fau,fav;
    int colu,colv;
    int sizu,sizv;
}stk[200001];
int top;
int siz[200001];
int fa[200001];
int col[200001];
int findfa(int x){
    return x==fa[x]?x:findfa(fa[x]);
}
int findcol(int x){
    return x==fa[x]?col[x]:(col[x]?findcol(fa[x]):!findcol(fa[x]));
}
bool merge(int u,int v){
    int x=findfa(u),y=findfa(v);
    int col1=findcol(u),col2=findcol(v);
    if(x==y){
        if(col1==col2)return false;
        return true;
    }
    int son,root;
    if(siz[x]<siz[y]){
        son=x,root=y;
    }
    else root=x,son=y;
    stk[++top]=(node){root,son,fa[root],fa[son],col[root],col[son],siz[root],siz[son]};
    siz[root]+=siz[son];
    fa[son]=root;
    if(col1==col2)col[son]=!col[son];
    return true;
}
struct qwq{
    int v;
    int nxt;
}edge[200001];
int cnt=-1;
int head[200001];
void add(int u,int v){
    edge[++cnt].nxt=head[u];
    edge[cnt].v=v;
    head[u]=cnt;
    //cout<<edge[cnt].nxt<<endl;
}
bool uni(int l,int r,int a,int b){
    for(int i=l;i<=r;++i){
        for(int j=head[i];~j;j=edge[j].nxt){
            //cout<<j<<" "<<edge[j].nxt<<endl;
            if(!j&&!edge[j].nxt)exit(0);
            int v=edge[j].v;
            if(a<=v&&v<=b)continue;
            if(!merge(i,v))return false;
        }
    }
    return true;
}
void reset(int x){
    while(top>x){
        node tmp=stk[top--];
        int u=tmp.u,v=tmp.v;
        fa[u]=tmp.fau,fa[v]=tmp.fav;
        col[u]=tmp.colu,col[v]=tmp.colv;
        siz[u]=tmp.sizu,siz[v]=tmp.sizv;
    }
}
bool ans[200001];
void solve(int l,int r,bool flag){
    if(l==r){
        ans[l]=flag;
        return;
    }
    int mid=(l+r)/2;
    int pre=top;
    bool fl=flag&&uni(mid+1,r,l,mid);
    solve(l,mid,fl),reset(pre);
    fl=flag&&uni(l,mid,mid+1,r);
    solve(mid+1,r,fl),reset(pre);
}
int n,m;
void init(){
    cnt=-1,top=0;
    for(int i=1;i<=n;++i){
        head[i]=-1;
        siz[i]=col[i]=1;
        fa[i]=i;
    }
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<=m;++i){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v),add(v,u);
        }
        solve(1,n,1);
        for(int i=1;i<=n;++i){
            printf("%d",ans[i]);
        }
        puts("");
    }
}

原文地址:https://www.cnblogs.com/youddjxd/p/11351327.html

时间: 2024-11-05 10:24:14

noip2019集训测试赛(四)的相关文章

[补档]noip2019集训测试赛(十五)

Problem A: 传送带 Time Limit: 1000 ms Memory Limit: 256 MB Description 在一个二维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.小y在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R.现在,小y想从A点走到D点,请问他最少需要走多长时间. Input 第一行是4个整数,表示A和B的坐标,分别为Ax,Ay,Bx,By. 第二行是4个整数,表示C和D的坐标,分别为Cx,Cy,D

noip2019集训测试赛(二)

Problem A: 余数 Time Limit: 1000 ms Memory Limit: 512 MB Description Input Output Sample Input 3 4 Sample Output 4 HINT Solution 那个所谓\(\sqrt n\)的东西叫做整除分块. 显然对于\(n÷i\),当$i<=\sqrt n $时,每个i都有一种取法 当\(n>=i>\sqrt n\),i每加\(\sqrt n\),\(n÷i\)的值就+1 然后就可以根号时间

noip2019集训测试赛(七)

Problem A: Maze Time Limit: 1000 ms Memory Limit: 256 MB Description 考虑一个N×M的网格,每个网格要么是空的,要么是障碍物.整个网格四周都是墙壁(即第1行和第n行,第1列和第m列都是墙壁),墙壁有且仅有两处开口,分别代表起点和终点.起点总是在网格左边,终点总是在网格右边.你只能朝4个方向移动:上下左右.数据保证从起点到终点至少有一条路径. 从起点到终点可能有很多条路径,请找出有多少个网格是所有路径的必经网格. Input 第一

[补档]noip2019集训测试赛(十二)

Problem A: 记忆(memory) Time Limit: 1000 ms Memory Limit: 512 MB Description 你在跟朋友玩一个记忆游戏. 朋友首先给你看了n个长度相同的串,然后从中等概率随机选择了一个串. 每一轮你可以询问一个位置上的正确字符,如果能够凭借已有的信息确定出朋友所选的串,那么游戏就结束了,你的成绩就是所用的轮数. 由于你实在太笨,不会任何策略,因此你采用一种方法,每次等概率随机询问一个未询问过的位置的字符. 现在你想知道,在这种情况下,你猜出

[补档]noip2019集训测试赛(八)

Problem B: 2048 Special Judge Time Limit: 1000 ms Memory Limit: 256 MB Description 2048曾经是一款风靡全球的小游戏. 今天,我们换一种方式来玩这个小游戏. 现在,你有一个双端队列,你只能把元素从左端或从右端放入双端队列中.一旦放入就不得取出.放入后,若队列中有连续两个相同的元素,它们将自动合并变成一个新的元素--原来那两个元素的和.若新的元素与它相邻的元素相同,则继续合并-- 如:双端队列中有2, 4, 16三

【2016北京集训测试赛(八)】 crash的数列

Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多点数列套着来加速转移呢? 但是发现好像太多数列套起来是可以烦死人的...... 我们就采用嵌套两次吧,记原数列为A,第一层嵌套为B,第二层嵌套为C. 我们其实可以发现一些规律,对于Ci,它对应了B中i的个数:对于Bi,它对应了A中i的个数. 稍加处理即可,我们一边计算一边模拟数列的运算,同时可以计算

2016集训测试赛(二十五)小结

这场测试赛有必要小结一下. 昨晚 1 点才睡, 今天状态很差, 先睡了 1 个小时, 然后开始打比赛. 第一题不大会做, 写了一个代码后发现是错的, 第二题看不懂题, 第三题简单地分析了一下, 发现是一个树形DP . 然后做 T3 , 大概推了很久, 写了很久, 又写了几个对拍, 搞到 11 点才搞掂. 这时候我发现 T1 有 50 分是我可做的, 然后 T2 的题意仍然不是很明确, 我想尝试着写写 T2 , 这个必须要写出来才能看出题意是不是这样, 写着写着发现 T2 的题意不是我理解的这样,

[北京集训测试赛(三)]灯(Light)-奇怪乱搞数学题-素数

Problem 灯 题目大意 n盏灯排成一列,标号一到n,一开始标号为1的灯亮着. 现在依次对于2~n的每一个质数pi,指定一盏亮着的灯ai,点亮所有标号为$A[i]\pm kP_i$的灯. 有spj,任意一种方案即可. 输入一个整数n,输出点灯方案. Solution 首先写个暴力,考虑一下小范围的数据. 我们发现$n<16$的时候没有完美解,都是n-1.再算下去,发现$n>16$的时候任意一组都有完美解. 我们分析一下这个玩意儿. 把一到n的灯集体下标前移1,变成0~n-1.这时候当我们点

【2016北京集训测试赛(八)】直径

注意:时限更改为4s 题解 考虑最原始的直径求法:找到离根节点(或离其他任意一点)最远的节点far1,再从far1出发找到离far1最远的节点far2,far1至far2的距离即为直径. 题目中提到了将原树的子树复制成新子树这一操作,显然如果我们将子树真正复制出来是会爆炸的. 实际上我们可以将每棵新子树中,真正有用的节点提取出来,以简化每个子树的结构,再按照题目的要求连接各个新子树. 我们用虚树来重构每一棵子树.每棵子树的虚树的关键点应至少包含: 子树的根节点. 这棵子树内部的直径的两端节点.