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("");
}
}