做CF被血虐。。。下次要扳回来!
AB题就pass吧,现在直接切入正题(A掉AB题我都花了1个小时啊,混蛋。。)
C
给你一个竞赛图,要你找一个三元环。
solution
竞赛图的意思就是 Aij≠Aji ,一直都忘记用这个性质,悲催。。
下面讲一个我翻别人代码翻到的最优美的方法:
维护一个序列B,这个序列对于任意 i<j 满足 A[B[i],B[j]]=1。
一开始序列为空,然后把1到N一次插入这个序列,比如现在插入i号元素:
1.找到一个最小的L,满足A[i,B[L]]=1;
2.找到一个最大的R,满足A[i,B[R]]=0;
3.如果L=R+1,就吧i插入到L,R之间,如果L≠R,那么我们就找到了一个环,因为L<R,所以A[B[L],B[R]]=A[B[R],i]=A[i,B[L]]=1。
#include <vector>
#include <cstdio>
using namespace std;
char a[5010][5010];
int main() {
int n,i;
scanf("%d",&n);
for (i=0;i<n;i++) scanf("%s",a[i]);
vector<int> b(1,0);
for (i=1;i<n;i++) {
int l=0,r=i-1;
while (l<i && a[i][b[l]]=='0') l++;
while (r>=0 && a[i][b[r]]=='1') r--;
if (l==r+1) b.insert(b.begin()+l,i);
else {printf("%d %d %d\n",b[l]+1,b[r]+1,i+1);break;};
};
if (i==n) printf("-1\n");
return 0;
}
D
题意有点复杂。。。
类似于线段树,只不过不需要吧节点建出来。
每个节点的数一定是一个等差数列,所以只要在递归的过程中记下第一项就可以了。
#include <cstdio>
#include <algorithm>
using namespace std;
long long n,m,mo,l,r,u,v;
long long cal(long long a,long long b,long long c) {
a-=b;
if (a<0) a=-1; else a=a>>c;
long long B=b+a*(1ll<<c);
long long s;
if ((++a)&1) s=((B+b)/2%mo)*(a%mo);
else s=((B+b)%mo)*(a/2%mo);
return s%mo;
}
long long cal(long long ll,long long rr,long long c,int d) {
if (u>n) return 0;
long long ans=0;
if (l>ll || r<rr) {
long long m=(ll+rr)>>1;
if (l<=m) ans+=cal(ll,m,c,d+1);
if (r>m) ans+=cal(m+1,rr,c+(1ll<<d),d+1);
if (ans>=mo) ans-=mo;
} else {
ans=cal(v,c,d)-cal(u-1,c,d);
if (ans<0) ans+=mo;
};
return ans;
}
int main() {
scanf("%I64d%I64d%I64d",&n,&m,&mo);
while (m--) {
scanf("%I64d%I64d%I64d%I64d",&l,&r,&u,&v);
v=min(v,n);
long long ans=cal(1,n,1,0);
printf("%I64d\n",ans%mo);
};
return 0;
}
E
给你一颗树+一条边,每条边有01两种状态,要求支持两种操作:
1.将从U到V的最短路上的边的01状态取反;
2.询问只考虑状态为1的边有多少个联通快;
如果没有那多出的一条边的话,也就是要求询问树中有多少条状态为0的边S,答案就是S+1;
加了一条边也差不多,答案是S,不过要特殊考虑树中的那一个环,如果这个环上的边状态全为1的话还要把答案额外加一。
因此我们直接删掉一条边做动态树就可以了。
第一次用C++写动态树,写了2个多小时,oh NO。。
另外疑问就是为什么CF不能用memset?老是编译错误?
#include <cstdio>
#include <algorithm>
#define N 100005
#define update(i) x[i]=x[l[i]]+x[r[i]]+a[i],y[i]=y[l[i]]+y[r[i]]+1-a[i];
using namespace std;
int t,T,L,mb,mdb,ans,d[N],z[N],g[N],o[N],fa[N],FA[N],p[N*2],next[N*2];
struct d_tree {
int g,t,l[N],r[N],z[N],a[N],x[N],y[N],fa[N],root[N];
void put(int i,int j) {if (i&&j) z[i]=!z[i],a[i]=!a[i],swap(x[i],y[i]);}
void lazy(int i) {put(l[i],z[i]),put(r[i],z[i]),z[i]=0;}
void rt(int i,int j) {
if (l[i]==j) l[i]=r[j],fa[r[j]]=i,r[j]=i;
else r[i]=l[j],fa[l[j]]=i,l[j]=i;
root[j]=root[i],root[i]=0;
if (l[fa[i]]==i) l[fa[i]]=j; else
if (r[fa[i]]==i) r[fa[i]]=j;
fa[j]=fa[i],fa[i]=j;
update(i);update(j);
}
void splay(int i,int j) {
if (root[i]) lazy(i);
while (!root[i]) {
lazy(fa[i]);
lazy(i);
rt(fa[i],i);
};
if (j) {
root[g=r[i]]=1;
r[i]=0;
update(i);
}
}
void access(int i) {
splay(t=i,1);
while (fa[i]) {
splay(t=fa[i],1);
r[t]=1,rt(t,i);
};
}
void cover(int i) {
access(i);
splay(L,0);
ans+=y[r[L]]-x[r[L]];
put(r[L],1);
}
} TREE;
void link(int a,int b) {next[++t]=d[a],d[a]=t,p[t]=b;}
int find(int i) {return (FA[i]==i)?i:FA[i]=find(FA[i]);}
void dfs(int i,int h) {
z[++t]=i,o[i]=t;
for (int k=d[i],j=p[k];k;k=next[k],j=p[k]) if ((h^k)!=1) {
if (o[j]) {
mb=k;
for (int I=o[j];I<=t;I++) g[++T]=z[I];
} else dfs(j,k);
};
t--;
}
void dfs2(int i,int h) {
for (int k=d[i],j=p[k];k;k=next[k],j=p[k]) if ((h^k)!=1 && ((k^mb)>1)) {
fa[j]=i;dfs2(j,k);
};
}
int main() {
int n,m,a,b,mdb=0;
t=1;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) {
scanf("%d%d",&a,&b);
link(a,b);link(b,a);
};
t=0;
dfs(1,0);
dfs2(g[1],0);
for (int i=0;i<N;i++) o[i]=0;
for (int i=1;i<=T;i++) o[g[i]]=i;
for (int i=1;i<=n;i++) FA[i]=(o[i])?i:fa[i];
for (int i=1;i<=n;i++) find(i);
for (int i=1;i<=n;i++) {
TREE.fa[i]=fa[i];
TREE.y[i]=TREE.root[i]=1;
};
g[0]=g[T],L=g[T+1]=g[1];
while (m--) {
scanf("%d%d",&a,&b);
int Fa=FA[a],Fb=FA[b];
int d1=abs(o[Fa]-o[Fb]);
int g1,g2;
if (o[Fa]<o[Fb]) g1=g[o[Fa]+1],g2=g[o[Fa]-1];
else g1=g[o[Fa]-1],g2=g[o[Fa]+1];
TREE.cover(a);
TREE.cover(b);
if (d1>T-d1 || ((d1==T-d1) && g1>g2)) {
mdb=!mdb;
if (mdb) ans++; else ans--;
TREE.cover(g[T]);
};
TREE.access(g[T]);
int al=TREE.x[g[T]]+mdb==T;
printf("%d\n",n-ans+al);
};
return 0;
}