noip2019集训测试赛(四)

Problem A: fibonacci

Time Limit: 3000 ms Memory Limit: 256 MB

Description

mi7NIe.png

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值