1、travel
基本把树剖忘光了。。。
注意到b树上的点至多对应a树上的一个点,询问要求b树上的一条路径对应a树上几条路径。
简单的说就是把树上的路径剖成logn段,每段我们都可以利用主席树对每段查询即可。
#include
#include
#include
#include
using namespace std;
const int Maxn=100005;
#define pb push_back
int W1[Maxn],W2[Maxn],W[Maxn*2],size[Maxn],q[Maxn];
int fa[Maxn],dep[Maxn],DEP[Maxn],sum[Maxn*20],top[Maxn];
int num[Maxn],nw[Maxn],stk[Maxn],v[Maxn*2],val[Maxn];
int son[Maxn*20][2],FA[Maxn][20],T[Maxn];
int n,m,Q,i,j,tot,l,r,cnt,x,u1,v1,u2,v2,ans,st,Z;
vector
e1[Maxn],e2[Maxn]; void read(){ tot=0; for (i=1;i<=n;i++) e1[i].clear(); for (i=2;i<=n;i++){ scanf("%d",&x); e1[x].pb(i); e1[i].pb(x); } for (i=1;i<=n;i++){ scanf("%d",&W1[i]); W[++tot]=W1[i]; } scanf("%d",&m); for (i=1;i<=m;i++) e2[i].clear(); for (i=2;i<=m;i++){ scanf("%d",&x); e2[x].pb(i); e2[i].pb(x); } for (i=1;i<=m;i++){ scanf("%d",&W2[i]); W[++tot]=W2[i]; } } bool cmp(const int &a,const int &b) { return size[a]>size[b]; } void Find_Heavy_Edge(){ for (q[l=r=1]=1,dep[1]=1;l<=r;l++){ int len=e1[q[l]].size(); for (i=0;i
0;r--){ size[q[r]]++; if (fa[q[r]]==0) continue; size[fa[q[r]]]+=size[q[r]]; } for (i=1;i<=n;i++) top[i]=i; for (l=1;l<=n;l++){ x = q[l]; if (e1[x].empty()) continue; sort(e1[x].begin(),e1[x].end(),cmp); int len=e1[x].size(); for (i=0;i
0){ x=stk[T]; int len=e1[x].size(); for (nw[x]++;nw[x]
>1; if (mid>=x) ins(son[p][0],son[q][0],l,mid,x); else ins(son[p][1],son[q][1],mid+1,r,x); } void Make_Tree(){ memset(FA,0,sizeof(FA)); for (q[l=r=1]=1,DEP[1]=1;l<=r;l++){ int len=e2[q[l]].size(); for (i=0;i
m) return; } memset(T,0,sizeof(T)); for (i=1,st=0;i<=m;i++){ x=q[i]; if (val[x]==0) T[x] = T[ FA[x][0] ]; else ins(T[ FA[x][0] ],T[x],1,n,val[x]); } } void work(){ Discretization(); Find_Heavy_Edge(); Connect_Heavy_Edge(); Correspond(); Make_Tree(); } int LCA(int x,int y){ if (DEP[x]
=0;i--) if (DEP[FA[x][i]]>=DEP[y]) x=FA[x][i]; for (int i=16;i>=0;i--) if (FA[x][i]!=FA[y][i]) x=FA[x][i], y=FA[y][i]; if (x==y) return x; return FA[x][0]; } int Query(int p1,int p2,int p3,int p4,int l,int r,int L,int R){ if (l>R || L>r) return 0; if (L<=l && R>=r) return sum[p1]+sum[p2]-sum[p3]-sum[p4]; int mid=(l+r)>>1; int ret1=Query(son[p1][0],son[p2][0],son[p3][0],son[p4][0],l,mid,L,R); int ret2=Query(son[p1][1],son[p2][1],son[p3][1],son[p4][1],mid+1,r,L,R); return ret1+ret2; } void solve(){ scanf("%d",&Q); ans=0; while (Q--){ scanf("%d%d%d%d",&u1,&v1,&u2,&v2); u1^=ans; v1^=ans; u2^=ans; v2^=ans; ans=dep[u1]+dep[v1]+DEP[u2]+DEP[v2]; Z=LCA(u2,v2); ans-=DEP[Z]*2-1; while (true){ if (top[u1]==top[v1]){ int x1=min(num[u1],num[v1]), x2=max(num[u1],num[v1]); ans-=Query(T[u2],T[v2],T[Z],T[ FA[Z][0] ],1,n,x1,x2); break; } else { if (dep[ top[u1] ]
2、弗洛伊德的复仇
可以看到如果所有的边都满足ai>bi(应该是优惠吧),那么一定存在最优策略使得所有的货物都应该是选择一条路径来运输。
但是题目里给了一条边ai<bi(这只鸽子似乎很讨厌大量的工作。。。),我们可能仍是选择上一种方案,或者可能选择一部分来给这只另类的鸽子运(注意这只鸽子运输的货物应为ai),其他的给另外的运,不难搞出大概的方案如下:
我们会得到三个图,各种求求最短路即可,最后枚举两个中点即可
(考场上居然忘了特判无解丢了好些分。。。。)
#include
#include
#include
using namespace std;
const int Maxn=10005, INF=1e9;
int node[Maxn],next[Maxn],a[Maxn],len[Maxn];
int Ds[Maxn],Dt[Maxn],q[100000],d1[105][105],d2[105][105];
int x[Maxn],y[Maxn],z1[Maxn],z2[Maxn],z3[Maxn];
int i,j,k,n,m,S,T,fw,tot,l,r,ans;
bool v[Maxn];
void add(int x,int y,int z){
node[++tot]=y; next[tot]=a[x]; a[x]=tot; len[tot]=z;
node[++tot]=x; next[tot]=a[y]; a[y]=tot; len[tot]=z;
}
void spfa(int S,int d[],int K){
for (i=1;i<=n;i++) d[i]=INF;
d[S]=0; v[S]=1;
for (q[l=r=1]=S;l<=r;v[q[l++]]=0){
for (i=a[q[l]];i;i=next[i])
if ( ((i&1)==K) && d[node[i]]>d[q[l]]+len[i]){
d[node[i]] = d[q[l]]+len[i];
if (!v[node[i]]) v[ q[++r]=node[i] ]=1;
}
}
}
int main(){
freopen("G.in","r",stdin);
freopen("G.out","w",stdout);
scanf("%d%d%d%d%d",&n,&m,&S,&T,&fw);
S++; T++;
int flag=0;
for (i=1,tot=0;i<=m;i++){
scanf("%d%d%d%d%d",&x[i],&y[i],&z1[i],&z2[i],&z3[i]);
x[i]++; y[i]++;
if (z1[i]
=fw) add(x[i],y[i],z1[i]*fw);
else add(x[i],y[i],z1[i]*z3[i]+z2[i]*(fw-z3[i]));
}
spfa(S,Ds,1);
spfa(T,Dt,0);
if (flag>0){
int t=z3[flag];
memset(d1,127/2,sizeof(d1));
for (i=1;i<=n;i++) d1[i][i]=0;
for (i=1;i<=m;i++)
if (z3[i]>=t) d1[x[i]][y[i]] = min( d1[x[i]][y[i]], z1[i]*t );
else d1[x[i]][y[i]] = min( d1[x[i]][y[i]], z1[i]*z3[i]+z2[i]*(t-z3[i]) );
for (k=1;k<=n;k++)
for (i=1;i<=n;i++) if (i!=k)
for (j=1;j<=n;j++)
if (i!=j && k!=j && d1[i][j]>d1[i][k]+d1[k][j])
d1[i][j] = d1[i][k]+d1[k][j];
memset(d2,127/2,sizeof(d2));
for (i=1;i<=n;i++) d2[i][i]=0;
for (i=1;i<=m;i++){
if (i==flag) continue;
if (z3[i]>=fw-t) d2[x[i]][y[i]] = min( d2[x[i]][y[i]], z1[i]*(fw-t) );
else d2[x[i]][y[i]] = min( d2[x[i]][y[i]], z1[i]*z3[i]+z2[i]*( (fw-t)-z3[i] ) );
}
for (k=1;k<=n;k++)
for (i=1;i<=n;i++) if (i!=k)
for (j=1;j<=n;j++)
if (i!=j && k!=j && d2[i][j]>d2[i][k]+d2[k][j])
d2[i][j] = d2[i][k]+d2[k][j];
ans=1000000000;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (d1[i][j]<100000000 && d2[i][j]<100000000)
ans=min(ans, Ds[i]+d1[i][j]+d2[i][j]+Dt[j]);
if (ans>100000000) puts("Impossible");
else printf("%d\n",ans);
} else
{
if (Ds[T]>100000000) puts("Impossible");
else printf("%d\n",Ds[T]);
}
return 0;
}
3、无尽之惩
把矩阵应用到bsgs里
我们可以把题目里的变换改成一个矩阵A。
题目里就是要求最小的k满足a*A^k=b,很明显的bsgs。
(不要搞啥A矩阵的逆矩阵,完全可以靠逆向模拟把b向前变换)
#include
#include
#include
#include
using namespace std;
typedef unsigned UI;
int a[50],b[50],c[50],t1[50],t2[50];
int s1,s2,i,j,k,t;
UI n,N,M,tmp,x,K[70000],KK[70000];
struct Matrix
{
int a[33][33];
void init(){
memset(a,0,sizeof(a));
for (i=0;i<33;i++)
a[i][i]=1;
}
Matrix operator *(const Matrix &x)const
{
Matrix ret;
UI column[33], row[33];
for (int i=0;i<33;i++){
column[i] = row[i] = 0;
for (int j=0;j<32;j++){
if (a[i][j]) column[i] |= ((UI)1<
0;n>>=1){ if (n&1) ret=ret*A; A=A*A; } return ret; } void Baby_Step_Giant_Step(){ N = 1<
<< i); K[0] = x; for (k=1;k<=M;k++){ for (i=0;i
0;i--) b[i] = b[i-1]; b[0] = x; for (i=0,x=0;i
<< i); K[k] = x; } memcpy(KK,K,sizeof(K)); sort(KK,KK+M+1); A = qck(A,M); C.init(); for (k=0;k<=M;k++,C=C*A){ for (i=0,tmp=0;i
M) {puts("poor sisyphus");return;} for (i=0;i<=M;i++) if (K[i]==tmp){ printf("%u\n",(UI)k*M+i); break; } } int main(){ freopen("punish.in","r",stdin); freopen("punish.out","w",stdout); while (~scanf("%u%d%d",&n,&s1,&s2)){ read(); init(); Baby_Step_Giant_Step(); } return 0; }
4、回文超能力
求字符串每向后加入一个字符会产生多少新回文串
——长知识了,原来有一个叫做回文树的东西,简直就是处理回文串的利器
(不会的同学可以去UOJ博客里找相关论文)
(有了这个东西APIO2014T1就变成模板题了)
#include
#include
#include
using namespace std;
const int Maxn=5000005;
char S[Maxn],ans[Maxn];
int son[Maxn][2],fail[Maxn],len[Maxn];
int n,last,i,st,x,cur,nw;
int newnode(int x){
son[st][0] = son[st][1] = 0;
len[st] = x;
return st++;
}
void init(){
gets(S+1);
n=strlen(S+1);
S[0]='#';
newnode( 0 );
newnode( -1 );
fail[0] = 1;
last = 0;
}
int get_fail(int x,int n){
while ( S[n-len[x]-1]!=S[n] ) x=fail[x];
return x;
}
int main(){
freopen("palin.in","r",stdin);
freopen("palin.out","w",stdout);
init();
for (i=1;i<=n;i++){
x = S[i]-'a';
cur = get_fail( last, i );
if ( !son[cur][x] ){
ans[i]='1';
nw = newnode( len[cur]+2 );
fail[nw] = son[ get_fail( fail[cur], i ) ][x];
son[cur][x] = nw;
} else ans[i]='0';
last = son[cur][x];
}
puts(ans+1);
return 0;
}
5、艾玛与乘积之和
又遇FFT,跪烂的节奏。
求n个数中任意k个数的乘积之和。
观察这个式子(1+a1*x)*(1+a2*x)*.....*(1+an*x),x^k前的系数就是k的答案。。。。
直接求的话复杂度是O(n^2)。把整个式子分成两部分,这样不停地分治下去,用FFT合并,这样就可以做到大约O(nlogn)
#include
#include
#include
#include
using namespace std;
const int Maxn=30005;
const int Mod=100003;
typedef long double LD;
int d[16][2][Maxn<<1];
int a[Maxn],ans[Maxn],i,n,q,x;
struct CP
{
LD x, y;
CP operator +(const CP &a)const
{ return (CP){x+a.x, y+a.y}; }
CP operator -(const CP &a)const
{ return (CP){x-a.x, y-a.y}; }
CP operator *(const CP &a)const
{ return (CP){x*a.x-y*a.y, x*a.y+y*a.x}; }
} A[Maxn<<2], B[Maxn<<2];
void FFT(CP A[],int N,int flag){
for (int i=1,j=0;i
>=1)); if (i>j) swap(A[i], A[j]); } for (int i=2;i<=N;i<<=1){ CP wn = (CP){ cos((LD)2*M_PI/i), flag*sin((LD)2*M_PI/i) }; for (int j=0;j
>1; solve(l,mid,e+1,d[e][0]); solve(mid+1,r,e+1,d[e][1]); if (e==0){ mid=(l+r)>>1; } int N , l0=mid-l+2, l1=r-mid+1; for (N=1;N<=l0+l1+1;N<<=1); for (int i=0;i
6、丛林前哨站
neerc原题,二分+半平面交,不多说
#include
#include
#include
using namespace std;
const int Maxn=50005;
const double Eps=1e-9;
int Case,n,i,j,L,R,Mid,ans;
int q[Maxn],l,r;
struct Point
{
double x,y;
void read(){ scanf("%lf%lf",&x,&y); }
Point operator +(const Point &a) const
{ return (Point){x+a.x, y+a.y}; }
Point operator -(const Point &a) const
{ return (Point){x-a.x, y-a.y}; }
Point operator *(const double &a) const
{ return (Point){x*a, y*a}; }
Point operator /(const double &a) const
{ return (Point){x/a, y/a}; }
} dot[Maxn];
struct Seg { int s, t; } line[Maxn];
inline double mult(Point P1,Point P2)
{ return (P1.x*P2.y-P1.y*P2.x); }
inline double mult(Point P0,Point P1,Point P2)
{ return mult(P1-P0,P2-P0); }
inline Point cross(Seg l1,Seg l2){
Point P0=dot[l1.s], P1=dot[l1.t];
Point Q0=dot[l2.s], Q1=dot[l2.t];
double u=mult(P0,P1,Q0), v=mult(P0,P1,Q1);
Point ret = (Q1*u - Q0*v) / (u-v);
return ret;
}
#define fabs(x) ((x)>0?(x):-(x))
inline bool out(Seg l0,Seg l1,Seg l2){
Point p0=dot[l1.s]-dot[l1.t];
Point p1=dot[l2.s]-dot[l2.t];
if ( fabs( (p0.x*p1.y)-(p1.x*p0.y) ) < Eps ) return 1;
Point DOT = cross(l1,l2);
return mult(dot[l0.s],dot[l0.t],DOT)
1);
}
int main(){
freopen("jungle.in","r",stdin);
freopen("jungle.out","w",stdout);
scanf("%d",&Case);
while (Case--){
scanf("%d",&n);
for (i=1;i<=n;i++)
dot[n-i+1].read();
L=1; R=n-2;
ans = n-2;
while (L<=R){
Mid=(L+R)>>1;
if (Judge()) ans=Mid, R=Mid-1;
else L=Mid+1;
}
printf("%d\n",ans);
}
return 0;
}
7、Circle
判断n个圆交是否为空。
跪跪跪跪跪跪。。。。。
本以为把圆的交点搞出来,然后半平面交。。。各种写跪!
陈老板讲了个很鬼畜的做法:先从x轴上找到交的区间[l,r],在区间里找到每个圆的最高点和最低点,这些区间也应是有交的。如果这些区间交为空,那么可以判断出这些圆在此处交为空,否则继续二分下去——实践证明这居然过了。。。。orzorzorz
标程给了一个极角区间求教的东东,似乎也没复杂度保证。。。。
#include
#include
#include
#include
using namespace std;
#define sqr(x) ( (x)*(x) )
const double Eps=1e-6;
struct Point{ double x,y; };
struct Seg{ int s, t; } line[20005];
struct Circle
{
Point p;
double r;
void read(){ scanf("%lf%lf%lf",&p.x,&p.y,&r); }
} cir[20005];
double L,R;
int Case,n,i;
bool Judge(double L,double R){
if (R-L
R) {puts("No");continue;} if ( fabs(R-L)
up) {puts("No"); continue;} else {puts("Yes"); continue;} } if ( Judge(L,R) ) puts("Yes"); else puts("No"); } return 0; }
8、Vector
好神的题目。。。。蒟蒻连暴力都不会打。。。。
观察到对于所有和相等的几何,一定是互不影响的。
不难发现最优方案一定是和相等的集合。(作为民科我就不证明了。。。。)
不难发现x和sum-x的方案数是一样的,这样方案数就是对总和呈正态分布。答案就是和为sum/2的方案数,用递推搞搞即可啦!
#include
#include
#include
using namespace std;
const int Mod=(1e9)+7;
int n,m,i,j,a[2005];
int f[2005][2505];
int main(){
freopen("vector.in","r",stdin);
freopen("vector.out","w",stdout);
scanf("%d",&n);
for (i=1,m=0;i<=n;i++){
scanf("%d",&a[i]);
m += a[i];
}
for (i=0;i<=m/2;i++) f[0][i]=1;
for (i=1;i<=n;i++)
for (j=0;j<=m/2;j++){
if (j<=a[i]) f[i][j] = f[i-1][j];
else f[i][j] = (f[i-1][j]-f[i-1][j-a[i]-1]+Mod)%Mod;
if (j>0) f[i][j] = (f[i][j]+f[i][j-1])%Mod;
}
printf("%d\n",(f[n][m/2]-f[n][m/2-1]+Mod)%Mod);
return 0;
}
9、LMC的项链
又是burnside计数
直接套用定理:所有置换下不动点的方案数的平均数。
要注意就是由于中间记录的书有可能超过模数p,那么需要将数x写成x=a*p^b,其中a与p互质,这样做逆元啥的比较方便。
#include
#include
#include
using namespace std;
const int Mod=10007;
int ans,n,a,b,i;
struct arr
{
int x, y;
arr operator *(const int &a)const{
arr ret = (*this);
int aa = a;
while (aa%Mod==0) aa/=Mod, ret.y++;
ret.x = ret.x*aa%Mod;
return ret;
}
arr operator *(const arr &a)const{
arr ret;
ret.x = x*a.x%Mod;
ret.y = y+a.y;
return ret;
}
} jc[200005], ny[200005];
int gcd(int a,int b){
while (b) b^=a^=b^=a%=b;
return a;
}
int qck(int a,int b){
int ret=1;
for (;b>0;b>>=1){
if (b&1) ret = ret*a%Mod;
a = a*a%Mod;
}
return ret;
}
void init(){
jc[0].x = 1;
jc[0].y = 0;
for (i=1;i<=n;i++)
jc[i] = jc[i-1]*i;
ny[0].x = 1;
ny[0].y = 0;
for (i=1;i<=n;i++){
ny[i].x = qck(jc[i].x, Mod-2);
ny[i].y = -jc[i].y;
}
}
int C(int n, int m){
if (m>n || m<0) return 0;
arr ret = jc[n] * ny[m] * ny[n-m];
if (ret.y!=0) return 0;
return ret.x;
}
int main(){
freopen("circle.in","r",stdin);
freopen("circle.out","w",stdout);
scanf("%d%d",&a,&b);
n = a+b;
init();
for (i=1;i<=n;i++)
if ( a%(n/gcd(i,n))==0 ){
ans = ( ans + C( gcd(i,n), a/(n/gcd(i,n)) ) ) %Mod;
//printf("rotate:%d %d\n",i,ans);
}
if (n&1){
ans = ( ans + n*C( n/2, a/2 )%Mod ) %Mod;
} else
{
if ((a&1)==0){
ans = ( ans + n/2*C( n/2, a/2 )%Mod ) %Mod;
ans = ( ans + n/2*C( n/2-1, a/2 )%Mod ) %Mod;
ans = ( ans + n/2*C( n/2-1, a/2-1 )%Mod ) %Mod;
} else
{
ans = ( ans + n*C( n/2-1, a/2 )%Mod ) %Mod;
}
}
for (i=0;i
10、Byc的游戏
欧拉路,注意判断图联通即可
#include
#include
#include
using namespace std;
char S[30005], T[30005];
int Case,n,i,d[50],fa[50],m1,m2;
bool v[50];
int gf(int x){
int xx=x, xxx;
while (xx!=fa[xx]) xx=fa[xx];
while (x!=xx) xxx=x, x=fa[x], fa[xxx]=xx;
return xx;
}
bool Judge(){
for (int i=0;i<26;i++)
if (d[i]!=0) return 0;
int tmp;
for (int i=0;i<26;i++)
if (v[i]) {tmp = gf(i);break;}
for (int i=0;i<26;i++)
if (v[i] && tmp!=gf(i)) return 0;
return 1;
}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d",&Case);
while (Case--){
scanf("%d\n",&n);
scanf("%s",S);
scanf("%s",T);
memset(d,0,sizeof(d));
memset(v,0,sizeof(v));
for (i=0;i<26;i++) fa[i]=i;
for (i=0;i
11、Ccl的排列
贴题解啦,写错个东东调得吐血
#include
#include
#include
using namespace std;
#define two(x) (1<<(x-1))
int n,a[105],i,j,k,cnt,s[105];
bool v[105];
struct BIGINT
{
int h;
int a[410];
void init(int x)
{
memset(a,0,sizeof(a));
h = 0;
while (x>0) a[h++]=x%10, x/=10;
}
bool operator >=(const BIGINT &x)const
{
if (h
x.h) return 1;
for (int i=h-1;i>=0;i--){
if (a[i]
x.a[i]) return 1; } return 1; } BIGINT operator +(const BIGINT &x)const { BIGINT ret; ret.init(0); int p = 0; for (int i=0;i
0) ret.a[ ret.h++ ] = p; return ret; } BIGINT operator -(const BIGINT &x)const { BIGINT ret; ret.init(0); for (int i=0;i
0 && ret.a[ret.h-1]==0) ret.h--; return ret; } BIGINT operator *(const BIGINT &x)const { BIGINT ret; ret.init(0); if (h==0 || x.h==0) return ret; for (int i=0;i
0 && ret.a[ret.h-1]==0) ret.h--; return ret; } BIGINT operator /(const BIGINT &x)const { BIGINT ret; ret.init(0); BIGINT p; p.init(0); BIGINT w; w.init(10); for (int i=h-1;i>=0;i--){ p = p*w; p.a[0]=a[i]; if (p.h==0) p.h++; while (p>=x) ret.a[i]++, p = p-x; } for (int i=h-1;i>=0;i--) if (ret.a[i]>0) {ret.h=i+1; break;} return ret; } void print() { if (h==0) {printf("0\n");return;} for (int i=h-1;i>=0;i--) printf("%d",a[i]); printf("\n"); } } g[105][105], f[105][105], jc[205], ans; BIGINT C(int n,int m){ BIGINT ret; ret.init(0); if (m>n || m<0) return ret; ret = jc[n]/jc[m]/jc[n-m]; return ret; } int main(){ freopen("per.in","r",stdin); freopen("per.out","w",stdout); scanf("%d",&n); for (i=1;i<=n;i++) scanf("%d",&a[i]); for (i=1,jc[0].init(1);i<=n*2;i++){ BIGINT tmp; tmp.init(i); jc[i] = jc[i-1]*tmp; } BIGINT tmp; tmp.init(2); for (i=1;i<=n;i++) if (!v[i]){ ++cnt; for (s[cnt]=0,k=i;!v[k];k=a[k]) v[k]=1, s[cnt]+=2; for (j=0;j<=s[cnt]/2;j++) g[cnt][j] = C(s[cnt]-j,j) + C(s[cnt]-j-1,j-1); } for (i=0;i<=cnt;i++) f[i][0].init(1); for (i=1;i<=cnt;i++) for (j=1;j<=n;j++) for (k=0;k<=s[i]/2&&k<=j;k++) f[i][j] = f[i][j] + (f[i-1][j-k] * g[i][k]); for (i=0;i<=n;i+=2) ans = ans + (jc[n-i]*f[cnt][i]); for (i=1;i<=n;i+=2) ans = ans - (jc[n-i]*f[cnt][i]); ans . print(); return 0; }