校内集训作业题
2021.01.01
[] CF142D
考虑以 $F,G$ 之间的距离为石子个数,则直接跑 $K-Nim$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=111; int N,M,A[MAXN],K; char str[MAXN]; int main(){ N=read(),M=read(),K=read(); bool RR=0,GG=0,GGG=0; for(int i=1;i<=N;i++){ scanf("%s",str+1); int totR=0,totG=0,psR=0,psG=0; for(int j=1;j<=M;j++){ if(str[j]=='R') totR++,psR=j; if(str[j]=='G') totG++,psG=j,GGG=1; } if(totR&&totG){A[++A[0]]=max(psR,psG)-min(psR,psG)-1;continue;} if(M==totR) RR=1; if(M==totG) GG=1; } if(A[0]){ bool ff=1; for(int i=10;i>=0;i--){ int tot=0; for(int j=1;j<=A[0];j++) if(A[j]&(1<<i)) tot++; ff&=(tot%(K+1)==0); } if(ff) printf("Second\n"); else printf("First\n"); return 0; } if(!GGG){printf("Second\n");return 0;} if(!GG&&!RR) printf("Draw\n");else if(GG) printf("Second\n");else if(RR) printf("First\n");return 0; }
[] CF512E
很自然想到我们可以规到 $1$ 与其他点均含边,对于与 $1$ 有边的点 $u,v$ ,若 $|u-v|>1$ ,则他们之间肯定存在一条边,则反转这条边就会得到一条 $1-p$ 的边。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e3+11; int E[MAXN][MAXN],N,tmp[MAXN],Num[MAXN]; vector<pii> Ans[2]; bool check(){for(int i=3;i<N;i++) if(!E[1][i]) return 0; return 1;} pii Do(int X,int Y){ memset(Num,0,sizeof(Num)); for(int i=1;i<=N;i++) if(E[X][i]) Num[i]=1; for(int i=2;i<=N;i++) if(E[Y][i]&&Num[i]){ E[X][Y]=E[Y][X]=0; E[1][i]=E[i][1]=1; return mp(1,i); } } void solve(int opt){ for(int i=1;i<=N-1;i++) E[i][i+1]=E[i+1][i]=1; E[1][N]=E[N][1]=1; while(!check()){ tmp[0]=0; for(int i=2;i<=N;i++) if(E[1][i]) tmp[++tmp[0]]=i; for(int i=2;i<=tmp[0];i++){ if(tmp[i]-tmp[i-1]>1){ pii p=Do(tmp[i-1],tmp[i]); if(!opt) Ans[0].pb(mp(tmp[i-1],tmp[i])); else Ans[1].pb(p); break; } } }return; } int main(){ //freopen("3.in","r",stdin); N=read(); for(int i=1;i<=N-3;i++){int u=read(),v=read();E[u][v]=E[v][u]=1;} solve(0);memset(E,0,sizeof(E)); for(int i=1;i<=N-3;i++){int u=read(),v=read();E[u][v]=E[v][u]=1;} solve(1); printf("%lu\n",Ans[0].size()+Ans[1].size()); for(int i=0;i<Ans[0].size();i++) printf("%d %d\n",Ans[0][i].fi,Ans[0][i].se); for(int i=Ans[1].size()-1;i>=0;i--) printf("%d %d\n",Ans[1][i].fi,Ans[1][i].se); }
[x] CF605E
神仙题目。
我们设 $E_u$ 表示 $u$ 到 $n$ 的期望最短时间。假设我们知道了所有点的 $E$ 值,则我们发现一个点 $u$ 可以走到点 $v$ 的条件是 $E_u>E_v$ ,则转移图是个 $DAG$ 。
我们如何去维护上述过程,显然 $u$ 会走一个期望最小的。 那么我们设 $f_u$ 表示在 $u$ 号结点且 $E_u>E_v$ 的所有转移代价。
由于时间的独立性可得 $$f_u=\sum E_v\times p_{u,v}\prod_x (1-p_{u,x})$$ $x$ 代表 $E$ 值在 $v$ 前面的数。
而对于 $E_u$ 因为存在时刻1无边可走则 $E_u=P(E_u+1)+f_u$ ,$P$ 值啥边都没有的概率。解得 $E_u=\dfrac{f_u+P}{1-P}$ ,暴力维护。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e3+11; int N,vis[MAXN]; double p[MAXN][MAXN]; double f[MAXN],now[MAXN]; double calc(int u){return (f[u]+now[u])/(1-now[u]);} void update(int u){ vis[u]=1; for(int i=1;i<=N;i++) if(!vis[i]) f[i]+=(calc(u)+1)*p[i][u]*now[i],now[i]*=(1-p[i][u]); return; } int main(){ N=read(); for(int i=1;i<=N;i++) for(int j=1;j<=N;j++){int x=read();p[i][j]=(double)x/100;} if(N==1){printf("0\n");return 0;} for(int i=1;i<N;i++) now[i]=1;update(N); while(1){ int ps=0; for(int i=1;i<=N;i++) if(!vis[i]) if(!ps||calc(i)<calc(ps)) ps=i; //cerr<<"ps:"<<ps<<" calc:"<<calc(ps)<<endl; update(ps); if(ps==1){printf("%.10lf\n",calc(1));return 0;} } }
2021.01.02
[] CF513E2
考虑单独计算每个数的贡献,可以发现 $s$ 值最优肯定是上升下降的,若出现连续 $3$ 个下降那么其实可以将中间的贡献舍去。
那么最后答案形式肯定是 $1,+2,-2,+2,-2,1$ 类似的,跑 $dp$ 就行。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define int long long #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e4+11; const int MAXK=211; int f[MAXN][MAXK][5],N,K,A[MAXN]; signed main(){ N=read(),K=read(); for(int i=1;i<=N;i++) A[i]=read(); memset(f,-127/3,sizeof(f)); for(int i=0;i<=3;i++) for(int j=0;j<=N;j++) f[j][0][i]=0; for(int i=1;i<=N;i++){ for(int j=1;j<=K;j++){ int s=1+(j!=1&&j!=K); f[i][j][0]=max(f[i-1][j-1][3],f[i-1][j][0])+s*A[i]; f[i][j][1]=max(f[i][j][0],f[i-1][j][1]); f[i][j][2]=max(f[i-1][j-1][1],f[i-1][j][2])-s*A[i]; f[i][j][3]=max(f[i][j][2],f[i-1][j][3]); if(s==2){ f[i][j][1]=max(f[i][j][1],f[i-1][j-1][1]); f[i][j][3]=max(f[i][j][3],f[i-1][j-1][3]); } } } printf("%lld\n",max(max(f[N][K][0],f[N][K][1]),max(f[N][K][2],f[N][K][3]))); return 0; }
[x] CF578D
我们考虑 $S$ 如何生成所有的答案串,可以将 $S$ 中选取一位删掉然后在剩下 $n$ 个位置中插入一个字符。
若对于 $S=aaa|bb|c$ ,可以发现若删除一个 $a$ 则 $S'=aa|bb|c$ ,在 $a,b$ 的间隔与第一个 $b$ 的后面如果都填 $b$ 则会算重,则我们只能填 $m-1$ 个字符对任意位置。
而这样也会算重,因为对于串类似 $abababab$ 会算重,因为将 $a$ 放在最后或把 $b$ 放在最前是一样的,所以在找一下减去这样的串。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; char str[MAXN]; int N,M,Ans; signed main(){ N=read(),M=read(); scanf("%s",str+1); for(int i=1;i<=N;i++) if(str[i]!=str[i-1]) Ans+=N*(M-1); int l=1; for(int i=2;i<=N+1;i++){ if(l==1){if(str[i]!=str[i-1]) l++;} else if(str[i]==str[i-2]) ++l; else{ //cerr<<"l:"<<l<<endl; Ans-=(l*(l-1))/2; if(str[i]!=str[i-1]) l=2; else l=1; } } printf("%lld\n",Ans); return 0; }
[x] CF757F
建出最短路树后问题转化为删掉 $u$ 后有多少个点走不到 $S$ ,支配树模板。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define int long long #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; vector<pii> vec[MAXN]; vector<int> Vec[MAXN],g[MAXN],G[MAXN]; priority_queue<pii> que; queue<int> Q; int dis[MAXN],N,M,S,vis[MAXN],d[MAXN],sta[MAXN],fa[MAXN][21],siz[MAXN],dep[MAXN]; void dijkstra(){ memset(dis,127/3,sizeof(dis)); que.push(mp(0,S)),dis[S]=0; while(!que.empty()){ int xx=que.top().se; que.pop(); if(vis[xx]) continue; vis[xx]=1; for(auto pp:vec[xx]){ int v=pp.fi,w=pp.se; if(dis[v]>dis[xx]+w){dis[v]=dis[xx]+w;que.push(mp(-dis[v],v));} } }return; } void topsort(){ Q.push(S);while(!Q.empty()){ int xx=Q.front(); Q.pop(); sta[++sta[0]]=xx; for(auto v:g[xx]){d[v]--;if(!d[v]) Q.push(v);} }return; } int Lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); for(int i=20;i>=0;i--) if(dep[fa[u][i]]>=dep[v]) u=fa[u][i]; if(u==v) return u; for(int i=20;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; return fa[u][0]; } void dfs(int u){siz[u]=1;for(auto v:Vec[u]) dfs(v),siz[u]+=siz[v];return;} signed main(){ N=read(),M=read(),S=read(); for(int i=1;i<=M;i++){int u=read(),v=read(),w=read();vec[u].pb(mp(v,w)),vec[v].pb(mp(u,w));} dijkstra(); for(int i=1;i<=N;i++) for(auto v:vec[i]) if(dis[v.fi]==dis[i]+v.se) g[i].pb(v.fi),G[v.fi].pb(i),d[v.fi]++;topsort(); dep[S]=1; for(int i=2;i<=sta[0];i++){ int u=sta[i],L=G[u][0]; for(int j=1;j<G[u].size();j++) L=Lca(L,G[u][j]); Vec[L].pb(u); dep[u]=dep[L]+1; fa[u][0]=L; for(int j=1;(1<<j)<=dep[u];j++) fa[u][j]=fa[fa[u][j-1]][j-1]; } dfs(S); int Maxn=0; for(int i=1;i<=N;i++) if(i!=S) Maxn=max(Maxn,siz[i]); printf("%lld\n",Maxn); return 0; }
2021.01.03
[] CF248E
设 $f_{i,j}$ 表示 $i$ 号杯内有 $j$ 个未吃过一次的概率。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,A[MAXN],all[MAXN],Q; double f[MAXN][111],Ans; double C(int a,int b){ double ans=1; for(int i=a;i>=a-b+1;i--) ans*=i; for(int i=2;i<=b;i++) ans/=i; return ans; } int main(){ //freopen("6.in","r",stdin); N=read(); for(int i=1;i<=N;i++) A[i]=read(),f[i][A[i]]=1; for(int i=1;i<=N;i++) Ans+=f[i][0],all[i]=A[i]; Q=read();while(Q--){ int u=read(),v=read(),k=read(); Ans-=f[u][0]; for(int i=0;i<=A[u];i++){ double s=0; for(int j=0;j<=k&&i+j<=all[u]&&i+j<=A[u];j++) s+=(double)(f[u][i+j]*C(i+j,j)*C(all[u]-i-j,k-j))/(double)C(all[u],k); f[u][i]=s; } all[u]-=k,all[v]+=k; Ans+=f[u][0]; printf("%.10lf\n",Ans); }return 0; }
[x] CF646D
可以发现每个是相同的,则我们只要专心弄一个将答案乘 $k$ 。
设 $f_{i,j}$ 表示前 $i$ 个中当前我们的装备在 $j$ 级的方案数。 转移复杂度 $O(n^2)$ ,无法通过此题。
而又因为本题允许有一定的精度误差?故可以将 $j$ 进行减少,在 $j$ 取 $1000$ 时 $eps$ 已经可以通过。
而还有个细节我们可以倒推 $f$ 使得每个概率都为 $1$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; double f[2][1001]; int N,K,cur; int main(){ N=read(),K=read(); for(int i=1;i<=N;i++){ cur^=1;memset(f[cur],0,sizeof(f[cur])); for(int j=1;j<=1000;j++){ double p1=(1/(double)K),p2=1-p1; f[cur][j]+=((double)(f[cur^1][j+1]+j)/(double)(j+1)+(double)(f[cur^1][j]+(double)(j+1)/2)*j/(double)(j+1))*p1; f[cur][j]+=f[cur^1][j]*p2; } } printf("%.10lf\n",f[cur][1]*K); return 0; }
[] CF906D
扩展欧拉定理模板题。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define int long long #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,p,A[MAXN],ps,Q,ste[MAXN],nex[MAXN]; int Get(int x){ int Ans=x; for(int i=2;i*i<=x;i++){ if((x%i)) continue; while((x%i)==0) x/=i; Ans/=i,Ans*=(i-1); } if(x!=1) Ans/=x,Ans*=(x-1); return Ans; } bool FF; int ksm(int a,int b,int mod){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;} bool pd(int l,int r,int Limit){// 1 < if(A[l]==1) return (1<Limit); int L=l,R=min(r,nex[l]-1),Up=A[R]; if(Up>=Limit) return 0; for(int i=R-1;i>=L;i--){ int res=A[i],cnt=Up; int s1=ksm(res,cnt,Limit),s2=ksm(res,cnt,Limit+1),s3=ksm(res,cnt,Limit+2); if(s1!=s2||s2!=s3||s1!=s3) return 0; Up=s1; }return 1; } int solve(int l,int r,int step){ if(A[l]==1) return A[l]%ste[step]; if(l==r) return A[l]%ste[step]; if(ste[step]==1) return 0; bool f=pd(l+1,r,ste[step+1]); if(!f) return ksm(A[l],solve(l+1,r,step+1)+ste[step+1],ste[step]); return ksm(A[l],solve(l+1,r,step+1),ste[step]); } signed main(){ //freopen("5.in","r",stdin); N=read(),p=read(); for(int i=1;i<=N;i++) A[i]=read(); ste[1]=p; for(int i=2;ste[i-1]!=1;i++) ste[i]=Get(ste[i-1]); ps=N+1; for(int i=N;i>=1;i--){nex[i]=ps;if(A[i]==1) ps=i;} Q=read();while(Q--){ int l=read(),r=read(); printf("%lld\n",solve(l,r,1)); } }
2020.01.04
[] CF113D
设 $f_{i,j}$ 表示 $A$ 在 $i$ ,$B$ 在 $j$ 的概率,对于初始化来说默认将 $f_{S,T}$ 先置为 $1$ 。大力高斯消元。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=23; int N,M,S,T; vector<int> vec[MAXN]; double p[MAXN],A[MAXN*MAXN][MAXN*MAXN]; int Q(int u,int v){return (u-1)*N+v;} void Init(){ for(int i=1;i<=N;i++){ for(int j=1;j<=N;j++){ if(i==j){ A[Q(i,i)][Q(i,i)]=1; continue; } A[Q(i,j)][Q(i,j)]=1-p[i]*p[j]; int cnt=0; cnt=vec[j].size(); for(auto v:vec[j]) A[Q(i,v)][Q(i,j)]-=p[i]*(1-p[j])/cnt; cnt=vec[i].size(); for(auto v:vec[i]) A[Q(v,j)][Q(i,j)]-=p[j]*(1-p[i])/cnt; cnt=vec[i].size()*vec[j].size(); for(auto v1:vec[i]) for(auto v2:vec[j]) A[Q(v1,v2)][Q(i,j)]-=(1-p[i])*(1-p[j])/cnt; } } return; } void Gause(){ for(int i=1;i<=N*N;i++){ int ps=-1; for(int j=i;j<=N*N;j++) if(A[j][i]!=0){ps=j;break;} for(int j=0;j<=N*N;j++) swap(A[i][j],A[ps][j]); for(int j=1;j<=N*N;j++){ if(j==i||A[j][i]==0) continue; double K=A[j][i]/A[i][i]; for(int s=0;s<=N*N;s++) A[j][s]-=A[i][s]*K; } } return; } void print(){ for(int i=1;i<=N*N;i++){ for(int j=0;j<=N*N;j++) cout<<A[i][j]<<" "; printf("\n"); }return; } int main(){ //freopen("A.in","r",stdin); N=read(),M=read(),S=read(),T=read(); for(int i=1;i<=M;i++){int u=read(),v=read();vec[u].pb(v),vec[v].pb(u);} for(int i=1;i<=N;i++) cin>>p[i]; Init(); A[Q(S,T)][0]=1; Gause(); for(int i=1;i<=N;i++) printf("%.10lf ",A[Q(i,i)][0]/A[Q(i,i)][Q(i,i)]); printf("\n"); return 0; }
[x] CF603E
可以发现一个图可以选出一个点集使得度数都为奇的充要条件是每个联通块都为偶数。
证明可以考虑我们将度数想成异或 $1$ ,那么问题变成从全 $0$ 转换成全 $1$ 。则我们每次可以选择联通块中任意两个异或 $1$ 。
则对于偶数联通快很容易做到这一点,而对于奇数联通快无法做到。 因为最后会有 $n$ 个 $1$ ,而我们每次会让 $1$ 的个数 $+2,-2,+0$ 无法变成奇数,故得证。
我们发现答案是非严格单调不升,可以选择 $lct$ 或整体二分来实现。
考虑如何整体二分,设 $solve(l,r,x,y)$ 表示只考虑询问在 $[l,r]$ 中,且最后答案在 $[x,y]$ 中的情况,利用可撤销并查集维护。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<stack> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e5+11; int N,M,Ans[MAXN],tmp[MAXN],W,Sum,id[MAXN]; struct node{int u,v,w,id;}E[MAXN],e[MAXN]; stack<pii> sta; struct Union{ int siz[MAXN],f[MAXN],Res[MAXN]; void init(){for(int i=1;i<=N;i++) siz[i]=1,f[i]=i,Res[i]=1;return;} int find(int x){return f[x]==x?x:find(f[x]);} void merge(int x,int y){ int t1=find(x),t2=find(y); if(t1==t2) return; if(siz[t1]<siz[t2]) swap(t1,t2); Sum-=(siz[t1]&1),Sum-=(siz[t2]&1); siz[t1]+=siz[t2];f[t2]=t1; Sum+=(siz[t1]&1); sta.push(mp(t1,t2));return; } void reset(){ int t1=sta.top().fi,t2=sta.top().se; sta.pop(); Sum-=(siz[t1]&1); siz[t1]-=siz[t2];f[t2]=t2; Sum+=(siz[t1]&1),Sum+=(siz[t2]&1); return; } }U; bool cmp(node x1,node x2){return x1.w<x2.w;} vector<int> vec[MAXN]; void solve(int l,int r,int x,int y){ //cerr<<"l:"<<l<<" r:"<<r<<" x:"<<x<<" y:"<<y<<endl; if(l>r) return; int mid=(l+r)>>1,siz=sta.size(); for(int i=l;i<=mid;i++) if(id[i]<x) U.merge(E[i].u,E[i].v); int ans=-1;for(int i=x;i<=y;i++){ if(e[i].id<=mid) U.merge(e[i].u,e[i].v); if(!Sum){ans=i;break;} } //cerr<<mid<<" "<<ans<<endl;//exit(0); if(ans==-1){ while(sta.size()!=siz) U.reset(); siz=sta.size(); for(int i=l;i<=mid;i++) if(id[i]<x) U.merge(E[i].u,E[i].v); solve(mid+1,r,x,y); while(sta.size()!=siz) U.reset(); return; }Ans[mid]=e[ans].w; while(sta.size()!=siz) U.reset(); siz=sta.size(); for(int i=l;i<=mid;i++) if(id[i]<x) U.merge(E[i].u,E[i].v); solve(mid+1,r,x,ans); while(sta.size()!=siz) U.reset(); siz=sta.size(); for(int i=x;i<ans;i++) if(e[i].id<l) U.merge(e[i].u,e[i].v); solve(l,mid-1,ans,y); while(sta.size()!=siz) U.reset(); return; } int main(){ //freopen("B.in","r",stdin); N=read(),M=read(); U.init(); Sum=N; for(int i=1;i<=M;i++) E[i].u=read(),E[i].v=read(),E[i].id=i,tmp[i]=E[i].w=read(),e[i]=E[i]; sort(e+1,e+M+1,cmp); for(int i=1;i<=M;i++) id[e[i].id]=i; solve(1,M,1,M); for(int i=1;i<=M;i++) if(!Ans[i]) Ans[i]=-1; for(int i=1;i<=M;i++) printf("%d\n",Ans[i]); return 0; }/* 4 4 1 3 4 2 4 8 1 2 2 3 4 3 */
[] CF1363F
之前做过。
可以发现一次操作 $[A_l,A_{l+1},...,A_r]$ 变为 $[A_r,A_l,A_{l+1},...,A_{r-1}]$ 可以看做将 $A_r$ 换到任意在它前面字符的前面。
则无解情况就是二者的某一字符出现次数不同,否则肯定有解。
可以看出 $A_r$ 最多会往前 $1$ 次,则我们让不动点最多,即选择最多点的点集使得其字符相同。
问题相当于求两个串的 $lcs$ ,而对于 $S_i=T_j$ 时我们需要取判断是否将 $i$ 点固定是否有解,即 $S$ 后缀的字符数均大于等于 $T$ 的字符数。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e3+11; int f[MAXN][MAXN],cas,N,suf[MAXN][27][2]; char S[MAXN],T[MAXN]; bool check(int x,int y){ for(int i=0;i<26;i++) if(suf[x][i][0]<suf[y][i][1]) return 0; return 1; } int main(){ //freopen("C.in","r",stdin); cas=read(); while(cas--){ N=read(); for(int i=0;i<=N;i++) for(int j=0;j<=N;j++) f[i][j]=-INT_MAX/3; scanf("%s%s",S+1,T+1); for(int i=0;i<=N;i++) for(int j=0;j<26;j++) for(int k=0;k<2;k++) suf[i][j][k]=0; for(int i=N;i>=1;i--){ for(int j=0;j<26;j++) suf[i][j][0]=suf[i+1][j][0],suf[i][j][1]=suf[i+1][j][1]; suf[i][S[i]-'a'][0]++,suf[i][T[i]-'a'][1]++; } bool ff=1; for(int i=0;i<26;i++) ff&=(suf[1][i][0]==suf[1][i][1]); if(!ff){printf("-1\n"); continue;} f[0][0]=0; for(int i=1;i<=N;i++) f[0][i]=f[i][0]=0; for(int i=1;i<=N;i++){ for(int j=1;j<=N;j++){ f[i][j]=max(f[i-1][j],f[i][j-1]); if(S[i]==T[j]&&check(i,j)) f[i][j]=max(f[i][j],f[i-1][j-1]+1); } } printf("%d\n",N-f[N][N]); }return 0; }
20200106
[x] CF600F
可以发现答案的下界肯定是度数的最大值,我们尝试是否能取到下界。
考虑归纳构造,当前已得到前 $i-1$ 条边的方案,我们对于 $(u,v)$ 分别求 $mex$ ,若颜色相同即成功。
否则,我们不断改变,即找到一条 $c,c',c,c',c,c',...$ 的增广路,而停止条件是不存在该颜色。
若可以一直循环的条件是我们会遇到一个环,即从 $v$ 开始走会走到 $u$ ,但是二分图无奇环,即不存在这样的边。
所以按上述方法构造,时间复杂度 $O(nm)$ 。 这个貌似叫做正则二分图 $k$ 染色。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXM=1e5+11; const int MAXN=2e3+11; int nex[MAXN][MAXN],M,A,B,U[MAXM],V[MAXM],d[MAXN],Ans; int main(){ A=read(),B=read(),M=read(); for(int i=1;i<=M;i++) U[i]=read(),V[i]=read()+A,d[U[i]]++,d[V[i]]++; for(int i=1;i<=A+B;i++) Ans=max(Ans,d[i]); printf("%d\n",Ans); for(int p=1;p<=M;p++){ int c1=-1,c2=-1,u=U[p],v=V[p]; for(int i=1;i<=Ans;i++) if(!nex[u][i]){c1=i;break;} for(int i=1;i<=Ans;i++) if(!nex[v][i]){c2=i;break;} nex[u][c1]=v,nex[v][c2]=u; if(c1==c2) continue; for(int c0=c2,i=v;i;i=nex[i][c0],c0^=c1^c2) swap(nex[i][c1],nex[i][c2]); } for(int i=1;i<=M;i++){ for(int j=1;j<=Ans;j++) if(nex[U[i]][j]==V[i]){printf("%d ",j);break;} }printf("\n"); return 0; }
[] CF809E
第一道自己做出来的莫比乌斯反演题!(虽然写了很久
$$
Ans=\sum_{i=1}^n\sum_{j=1}^n \phi(A_iA_j)\cdot dis_{i,j}=\sum_i\sum_j {V(\gcd(A_i,A_j))} \phi(A_i)\cdot \phi(A_j)\cdot dis_{i,j}
$$
其中 $V(x)=\prod_{p\in prime} (1-\dfrac{1}{p})$ 。
则
$$
Ans=\sum_x V_x\sum_i\sum_j [gcd(A_i,A_j)==x]\cdot \phi(A_i)\cdot \phi(A_j)\cdot dis_{i,j}\\=\sum_x V(x)f(x)
$$
其中 $f(x)=[gcd(A_i,A_j)==x]\cdot \phi(A_i)\cdot \phi(A_j)\cdot dis_{i,j}$ 。对 $f$ 反演,设 $F(x)=\sum_{x|d} f(d)$ 。
则
$$
F(x)=\sum_i\sum_j [x|A_i]\cdot [x|A_j]\cdot dis_{i,j}\cdot \phi(A_i)\cdot \phi A(j)
$$
而对于 $F(x)$ ,我们每次可以暴力把 $x|A_i$ 的 $i$ 点摘出来,每个点对在 $lca$ 处出现一次,故可以用虚树维护。
具体的,我们设摘出来的点为 $1-k$ ,则 $F(x)=\sum_i\sum_j \phi(A_i)\cdot \phi(A_j)\cdot(dep_i+dep_j-2dep_{LCA})$ ,维护 $\sum \phi(A)$ 与 $\sum \phi(A)\cdot dep_i$ 。
时间复杂度 $O(n\log^2 n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=4e5+11; int phi[MAXN],N,A[MAXN],id[MAXN],pri[MAXN],mu[MAXN],v[MAXN],inv[MAXN],V[MAXN]; vector<int> vec[MAXN],Vec[MAXN]; void Init(){ mu[1]=1,phi[1]=1; V[1]=1; for(int i=2;i<=N;i++){ if(!v[i]){pri[++pri[0]]=i,v[i]=i,mu[i]=-1,phi[i]=i-1;V[i]=inv[i-1]*i%mod;} for(int j=1;j<=pri[0]&&i<=N/pri[j];j++){ v[i*pri[j]]=pri[j]; if(!(i%pri[j])){phi[i*pri[j]]=phi[i]*pri[j];V[i*pri[j]]=V[i];mu[i*pri[j]]=0;break;} phi[i*pri[j]]=phi[i]*(pri[j]-1); mu[i*pri[j]]=-mu[i]; V[i*pri[j]]=V[i]*V[pri[j]]%mod; } } return; } int ksm(int a,int b){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;} int in[MAXN],out[MAXN],rev[MAXN],dep[MAXN],tot,Log[MAXN],F[MAXN],sta[MAXN],Q[MAXN],q[MAXN],f[MAXN]; vector<int> C; void dfs(int u,int fath){in[u]=++tot,rev[tot]=u,dep[u]=dep[fath]+1; for(auto v:vec[u]) if(v!=fath){dfs(v,u);rev[++tot]=u;} out[u]=tot;} bool cmp(int x,int y){return in[x]<in[y];} struct RMQ{ int Minn[MAXN][21],ps[MAXN][21]; void init(){ for(int i=1;i<=tot;i++) Minn[i][0]=dep[rev[i]],ps[i][0]=rev[i]; for(int j=1;(1<<j)<=tot;j++) for(int i=1;i+(1<<j)-1<=tot;i++){ if(Minn[i][j-1]<=Minn[i+(1<<(j-1))][j-1]) Minn[i][j]=Minn[i][j-1],ps[i][j]=ps[i][j-1]; else Minn[i][j]=Minn[i+(1<<(j-1))][j-1],ps[i][j]=ps[i+(1<<(j-1))][j-1]; } return; } int Qlca(int u,int v){ if(in[u]>in[v]) swap(u,v); int L=in[u],R=in[v],len=R-L+1,k=Log[len]; int w1=Minn[L][k],w2=Minn[R-(1<<k)+1][k]; if(w1<=w2) return ps[L][k]; return ps[R-(1<<k)+1][k]; } }S; void ins(int x){ if(sta[0]==1){if(sta[1]!=x) sta[++sta[0]]=x;return;} int now=S.Qlca(x,sta[sta[0]]); while(sta[0]>1&&dep[sta[sta[0]-1]]>=dep[now]) Vec[sta[sta[0]-1]].pb(sta[sta[0]]),sta[0]--; if(dep[sta[sta[0]]]>dep[now]) Vec[now].pb(sta[sta[0]]),sta[0]--; if(sta[sta[0]]!=now) sta[++sta[0]]=now; sta[++sta[0]]=x; return; } void build(){ sta[0]=0;sta[++sta[0]]=1; for(int i=1;i<=Q[0];i++)ins(Q[i]); while(sta[0]>1) Vec[sta[sta[0]-1]].pb(sta[sta[0]]),sta[0]--; return; } int S1[MAXN],S2[MAXN],res,D; // S1 dep*V S2 V void Clear(){for(auto v:C) Vec[v].clear(),S1[v]=S2[v]=0;C.clear();return;} void dfs1(int u){ int now1=0,now2=0; C.pb(u); for(auto v:Vec[u]){ dfs1(v); if(!(A[u]%D)) now2+=(S1[v]*phi[A[u]])%mod,now2-=phi[A[u]]*S2[v]%mod*dep[u]%mod,now2=((now2%mod)+mod)%mod; now1-=2*dep[u]*S2[u]%mod*S2[v]%mod,now1=(now1%mod+mod)%mod; now1+=S1[u]*S2[v],now1+=S2[u]*S1[v],now1%=mod; S1[u]+=S1[v],S2[u]+=S2[v]; S1[u]%=mod,S2[u]%=mod; } if(!(A[u]%D)) S1[u]+=dep[u]*phi[A[u]]%mod,S2[u]+=phi[A[u]]; S1[u]%=mod,S2[u]%=mod; res+=(now1+now2)*2%mod; return; } signed main(){ //freopen("1.in","r",stdin); N=read(); for(int i=1;i<=N;i++) A[i]=read(),id[A[i]]=i; inv[1]=1; for(int i=2;i<=N;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod; for(int i=1;i<N;i++){int u=read(),v=read();vec[u].pb(v),vec[v].pb(u);} Init(); dfs(1,0); Log[0]=-1; for(int i=1;i<=tot;i++) Log[i]=Log[i>>1]+1; S.init(); for(int i=1;i<=N;i++){ D=i;Q[0]=0,sta[0]=0; bool ff=0;for(int j=i;j<=N;j+=i) Q[++Q[0]]=id[j]; sort(Q+1,Q+Q[0]+1,cmp); build(); res=0; //cerr<<Q[0]<<" "<<i<<endl; dfs1(1); F[i]=res; Clear(); } for(int i=1;i<=N;i++) for(int j=i;j<=N;j+=i) f[i]+=mu[j/i]*F[j],f[i]=(f[i]%mod+mod)%mod; int Ans=0; for(int i=1;i<=N;i++) Ans+=V[i]*f[i],Ans%=mod; printf("%lld\n",Ans*ksm(N*(N-1)%mod,mod-2)%mod); return 0; }/* 3 1 2 3 1 2 1 3 */
[x] CF814E
显然,若我们按照最短路进行分层每层点是连续的且每个点都仅和上一层的一个点连边。
很简单的想法是设 $f_{i,j}$ 表示当前层的区间为 $[i-j+1,i]$ 的方案数,而我们发现每一层的状态还与下一层点的个数有关。
故可以设 $g_{i,c2,c3}$ 表示当前层有 $i$ 个结点,且上一层中有 $c2$ 个度数为 $2$ 的点,有 $c3$ 个度数为 $3$ 的点,且连的只有上层点的边与这层与上层的连边的方案数。
故我们修改 $f$ 的定义,将其设为不管当前层的连边的方案数。 则 $f_{i,j}=\sum f_{i-j,k}\times g_{j,c2,c3}$ ,$c2$ 指 $[i-j-k+1,i-j]$ 中度数为 $2$ 的点的个数,$c3$ 同理。
则我们只要求 $g$ ,由于 $n\leq 50$ ,乱写个 $dp$ 就过了?实现精细一点可以做到 $O(n^3)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=51; const int inv2=(mod+1)>>1; int N,d[MAXN],S2[MAXN],S3[MAXN],f[MAXN][MAXN],g[MAXN][MAXN][MAXN]; int fac[MAXN],ifac[MAXN],inv[MAXN]; int C(int a,int b){return fac[a]*ifac[b]%mod*ifac[a-b]%mod;} signed main(){ //freopen("C.in","r",stdin); N=read(); for(int i=1;i<=N;i++) d[i]=read(); fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=1; for(int i=2;i<=N;i++) fac[i]=fac[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod,ifac[i]=ifac[i-1]*inv[i]%mod; for(int i=1;i<=N;i++){ S2[i]=S2[i-1],S3[i]=S3[i-1]; if(d[i]==2) S2[i]++; else S3[i]++; } g[0][0][0]=1; for(int i=1;i<=N;i++) for(int j=2;j<=i;j++) if(i-j-1>=0) g[0][0][i]+=g[0][0][i-j-1]*C(i-1,j)%mod*fac[j]%mod*inv2%mod; for(int i=1;i<=N;i++){ for(int j=0;j<=N;j++){ if(i>=2) g[0][i][j]+=(i-1)*g[0][i-2][j]%mod; if(j>=1) g[0][i][j]+=j*g[0][i][j-1]%mod; g[0][i][j]%=mod; } } for(int i=1;i<=N;i++){ for(int j=0;j<=N;j++){ for(int k=0;k<=N;k++){ g[i][j][k]=g[i-1][j-1][k]*j+g[i-1][j+1][k-1]*k; g[i][j][k]%=mod; } } } f[1+d[1]][d[1]]=1; for(int i=2+d[1];i<=N;i++){ for(int j=1;j<=i;j++){ for(int k=1;k<=i-j-1;k++){ //i-j-k+1 i-j f[i][j]+=f[i-j][k]*g[j][S2[i-j]-S2[i-j-k]][S3[i-j]-S3[i-j-k]]; f[i][j]%=mod; } } } int Ans=0; for(int i=1;i<=N-1;i++) Ans+=f[N][i]*g[0][S2[N]-S2[N-i]][S3[N]-S3[N-i]],Ans%=mod; printf("%lld\n",Ans); return 0; }
20200107
[x] CF461E
可以发现对于对于 $B$ 来说肯定是能取就取的。所以我们只需要维护 $c1$ 字母开头,$c2$ 字母为第二串的开头的最小长度,即在 $SAM$ 上第一个走 $c1$ ,走 $c2$ 时会失配的最小长度。
由于值不会太大故我们只需要利用 $Trie$ 维护。 所以现在的问题是 $4$ 个点的完全图最多走少次使得总距离不超过 $n$ ,可以用过二分矩阵快速幂维护。 时间复杂度 $O(4^3\times n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,M; char S[MAXN]; struct Matrix{int A[4][4];void init(){memset(A,127/3,sizeof(A));return;}}F; Matrix operator*(Matrix x1,Matrix x2){ Matrix x3; x3.init(); for(int i=0;i<=3;i++) for(int j=0;j<=3;j++) for(int k=0;k<=3;k++) x3.A[i][j]=min(x3.A[i][j],x1.A[i][k]+x2.A[k][j]); return x3; } Matrix ksm(Matrix a,int b){ b--; Matrix Ans=a;while(b){if(b&1) Ans=Ans*a; a=a*a,b>>=1;}return Ans; } struct Trie{ int ch[MAXN*11][5],dep[MAXN*11],tot=1; void ins(int l){ int x=1; for(int i=1;i<=min(M-l+1,10ll);i++){ int c=S[l+i-1]-'A'; if(!ch[x][c]) ch[x][c]=++tot; dep[ch[x][c]]=dep[x]+1; x=ch[x][c]; }return; } void Query(int l){ int x=1; for(int i=1;i<=min(M-l+1,10ll);i++){ int c=S[l+i-1]-'A'; x=ch[x][c]; for(int j=0;j<=3;j++) if(!ch[x][j]) F.A[S[l]-'A'][j]=min(F.A[S[l]-'A'][j],dep[x]); }return; } }T; signed main(){ //freopen("1.in","r",stdin); N=read(); scanf("%s",S+1); M=strlen(S+1); for(int i=1;i<=M;i++) T.ins(i); F.init(); for(int i=1;i<=M;i++) T.Query(i); int l=1,r=N,res=0; /*for(int i=0;i<=3;i++){for(int j=0;j<=3;j++)printf("%d ",F.A[i][j]);printf("\n");} return 0;*/ while(l<=r){ int mid=(l+r)>>1; Matrix G=ksm(F,mid); int Minn=LLONG_MAX; for(int j=0;j<=3;j++) for(int k=0;k<=3;k++) Minn=min(Minn,G.A[j][k]); if(Minn<N){res=mid,l=mid+1;} else r=mid-1; } printf("%lld\n",res+1); return 0; }/* 5 ABCCAD */
[x] CF464E
考虑通过 $dijkstra$ 的方法更新,可以发现瓶颈是如何维护 $dis$ 数组。
而假设我们已经知道 $dis_u$ ,对于边 $(u,v,w)$ 我们要做的是 $dis_u+2^w$ 即在 $dis_u$ 二进制分解后快速把 $w$ 位后的连续 $1$ 置为 $0$ ,然后将后面的那个 $0$ 置为 $1$ 。
这个过程可以通过主席树优化,时间复杂度 $O(n\log^2 n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define LL long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+23; int rt[MAXN],pw[MAXN],Lim=1e5+20,N,M,Len[MAXN],SS,TT; struct Segment{ int Hash[MAXN*51],rs[MAXN*51],ls[MAXN*51],tot,siz[MAXN*51]; void pushup(int k,int l,int r){ int mid=(l+r)>>1,len=mid-l+1; siz[k]=siz[ls[k]]+siz[rs[k]]; Hash[k]=(LL)((LL)Hash[rs[k]]*pw[len]%mod+Hash[ls[k]])%mod; return; } void build0(int &k,int l,int r){ if(!k) k=++tot; Len[r-l+1]=k;if(l==r) return; int mid=(l+r)>>1; build0(ls[k],l,mid),build0(rs[k],mid+1,r); return; } void build1(int &k,int l,int r){ if(!k) k=++tot; if(l==r){Hash[k]=1,siz[k]=1;return;} int mid=(l+r)>>1; build1(ls[k],l,mid),build1(rs[k],mid+1,r); pushup(k,l,r);return; } bool cmp(int p,int q,int l,int r){ //cerr<<p<<" "<<q<<" "<<l<<" "<<r<<" "<<endl; if(l==r) return Hash[p]<Hash[q]; int mid=(l+r)>>1; if(Hash[rs[p]]!=Hash[rs[q]]) return cmp(rs[p],rs[q],mid+1,r); return cmp(ls[p],ls[q],l,mid); } void Modify(int &p,int l,int r,int ps){// ps 1 int las=p; p=++tot; if(l==r){siz[p]=Hash[p]=1;return;} ls[p]=ls[las],rs[p]=rs[las]; int mid=(l+r)>>1; if(ps<=mid) Modify(ls[p],l,mid,ps); else Modify(rs[p],mid+1,r,ps); pushup(p,l,r); return; } void Clear(int &p,int l,int r,int x,int y){ if(x<=l&&r<=y){p=Len[r-l+1];return;} int las=p; p=++tot; ls[p]=ls[las],rs[p]=rs[las]; int mid=(l+r)>>1; if(x<=mid) Clear(ls[p],l,mid,x,y); if(mid<y) Clear(rs[p],mid+1,r,x,y); pushup(p,l,r); return; } int Ans=-1; void Find1(int k,int l,int r){if(l==r){Ans=l;return;} int mid=(l+r)>>1; if(siz[ls[k]]==mid-l+1) Find1(rs[k],mid+1,r); else Find1(ls[k],l,mid);return;} void Find2(int k,int l,int r,int x,int y){ if(Ans!=-1) return; if(x<=l&&r<=y){if(siz[k]==r-l+1)return;Find1(k,l,r);return;} int mid=(l+r)>>1; if(x<=mid) Find2(ls[k],l,mid,x,y); if(Ans!=-1) return; if(mid<y) Find2(rs[k],mid+1,r,x,y); return; } int find(int id,int ps){Ans=-1;Find2(rt[id],0,Lim,ps,Lim);return Ans;} void Add(int id,int ps){int x=find(id,ps);if(ps<=x-1) Clear(rt[id],0,Lim,ps,x-1);Modify(rt[id],0,Lim,x);return;} }S; vector<pii> vec[MAXN]; struct node{int x;bool operator < (const node &p)const{return !S.cmp(rt[x],rt[p.x],1,Lim);}}O; priority_queue<node> que; int vis[MAXN],Que[MAXN],pre[MAXN]; vector<int> ANS; signed main(){ //freopen("maker.in","r",stdin); N=read(),M=read(); for(int i=1;i<=M;i++){int u=read(),v=read(),w=read();vec[u].pb(mp(v,w)),vec[v].pb(mp(u,w));} SS=read(),TT=read(); pw[0]=1; for(int i=1;i<=Lim;i++) pw[i]=(LL)pw[i-1]*2ll%mod; S.build0(rt[0],0,Lim);S.build1(rt[N+1],0,Lim);for(int i=1;i<=N;i++) rt[i]=rt[N+1]; O.x=SS; rt[SS]=rt[0]; que.push(O); Que[SS]=1; while(!que.empty()){ int xx=que.top().x; que.pop(); if(vis[xx]) continue;vis[xx]=1; if(xx==TT){ printf("%d\n",S.Hash[rt[xx]]); int ee=TT; while(ee) ANS.pb(ee),ee=pre[ee]; printf("%lu\n",ANS.size()); for(int i=ANS.size()-1;i>=0;i--) printf("%d ",ANS[i]); printf("\n"); return 0; } for(auto pp:vec[xx]){ int v=pp.fi,w=pp.se; if(vis[v]) continue;rt[N+2]=rt[xx]; S.Add(N+2,w); if(S.cmp(rt[N+2],rt[v],0,Lim)){rt[v]=rt[N+2]; pre[v]=xx;O.x=v;que.push(O);} } }printf("-1\n"); return 0; }
[] CF494C
很显然可以按照区间建树,设 $f_{i,j}$ 表示当前考虑到第 $i$ 个结点,且最大值 $\leq \max+j$ 的方案数,$\max$ 表示当前区间的最大值。
答案即为 $\sum (f_{1,i}-f_{1,i-1})\times (\max+i)$ 。而对于转移考虑 $i$ 结点是否选取。 时间复杂度 $O(q^2)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<map> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; const int MAXM=5e3+11; struct node{int l,r,w; double p;} tr[MAXN]; int tot,N,M,A[MAXN],Maxn,d[MAXN],Id[MAXN],Log[MAXN]; map<pii,bool> Ma; vector<int> vec[MAXN]; double f[MAXM][MAXM<<1]; bool cmp(node x1,node x2){return x1.r-x1.l>x2.r-x2.l;} struct RMQ{ int Maxn[MAXN][21]; void init(){for(int i=1;i<=N;i++) Maxn[i][0]=A[i]; for(int j=1;(1<<j)<=N;j++) for(int i=1;i+(1<<j)-1<=N;i++) Maxn[i][j]=max(Maxn[i][j-1],Maxn[i+(1<<(j-1))][j-1]);} int Query(int L,int R){if(!L) L=1; if(R==N+1) R=N;int k=Log[R-L+1];return max(Maxn[L][k],Maxn[R-(1<<k)+1][k]);} }S; void dfs(int u){ if(!d[u]){f[u][0]=1-tr[u].p;for(int i=1;i<=M;i++) f[u][i]=1;return;} int L=tr[u].l,R=tr[u].r,Max=S.Query(L,R); for(auto v:vec[u]) dfs(v); for(int i=0;i<=M;i++){ double p0=tr[u].p,p1=1-tr[u].p; for(auto v:vec[u]){ int max=S.Query(tr[v].l,tr[v].r); //cerr<<max<<endl; if(i+Max-max-1>=0&&i+Max-max-1<=M) p0*=f[v][i+Max-max-1]; else if(i+Max-max-1<0) p0=0; if(i+Max-max>=0&&i+Max-max<=M) p1*=f[v][i+Max-max]; else if(i+Max-max<0) p1=0; } if(!i) p0=0; f[u][i]=p0+p1; //cerr<<"i:"<<i<<" f:"<<p0+p1<<endl; }//exit(0);return; } int main(){ //freopen("C.in","r",stdin); Log[0]=-1; for(int i=1;i<MAXN;i++) Log[i]=Log[i>>1]+1; N=read(),M=read(); for(int i=1;i<=N;i++) A[i]=read(),Maxn=max(Maxn,A[i]); S.init(); for(int i=1;i<=M;i++){ tr[++tot].l=read(),tr[tot].r=read(); cin>>tr[tot].p; tr[tot].w=1; } tr[++tot].l=0,tr[tot].r=N+1,tr[tot].p=0; sort(tr+1,tr+tot+1,cmp); for(int i=2;i<=tot;i++) for(int j=i-1;j>=1;j--) if(tr[j].l<=tr[i].l&&tr[i].r<=tr[j].r) {vec[j].pb(i),d[j]++;break;} dfs(1); //for(int i=1;i<=N;i++) for(int j=i;j<=N;j++) printf("(%d,%d):%d\n",i,j,S.Query(i,j)); /*for(int i=1;i<=tot;i++){ for(int j=0;j<=M;j++) printf("%.3lf ",f[i][j]);printf("\n"); }*///return 0; double Ans=0; for(int i=0;i<=M;i++){ double p=f[1][i]; if(i) p-=f[1][i-1]; //cerr<<p<<" "<<S.Query(1,N)<<endl; Ans+=p*(S.Query(1,N)+i); } printf("%.10lf\n",Ans);return 0; } /* 3 2 1 2 3 1 3 0.500 1 2 0.800 */
20210109
[] CF285E
我们设 $f(i)$ 表示钦定 $i$ 个位置满足 $|p_i-i|=1$ 的方案数,$F(i)$ 表示恰好有 $i$ 个位置的方案数。
则 $f(i)=\sum_j \dbinom{j}{i} F(j)$ ,根据二项式反演可得 $F(i)=\sum_{j} (-1)^{j-i}\cdot \dbinom{j}{i}\cdot f(i)$ ,故现在只要求 $f(i)$ 。
对 $f(i)$ 我们可以将其看成一个二分图找出一个为 $i$ 的匹配,直接将二分图摘出来跑普通 $dp$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e3+11; int N,M,f[MAXN][MAXN][2],fac[MAXN],C[MAXN][MAXN]; signed main(){ //freopen("3.in","r",stdin); N=read(),M=read(); fac[0]=1; for(int i=1;i<=N;i++) fac[i]=fac[i-1]*i%mod; //2N-2 C[0][0]=1; for(int i=1;i<=N;i++){C[i][0]=1; for(int j=1;j<=N;j++) C[i][j]=C[i-1][j]+C[i-1][j-1],C[i][j]%=mod;} f[0][0][0]=1; for(int i=1;i<=2*N-2;i++){ for(int j=0;j<=N;j++){ //f[i][j][0]+=f[i-1][j-`1] f[i][j][0]+=f[i-1][j][0]+f[i-1][j][1]; if(j&&i!=N) f[i][j][1]+=f[i-1][j-1][0]; if(j&&i==N) f[i][j][1]+=f[i-1][j-1][0]+f[i-1][j-1][1]; f[i][j][0]%=mod,f[i][j][1]%=mod; } }// g_i int Ans=0,pw=1; for(int i=M;i<=N;i++){ int W=((f[2*N-2][i][0]+f[2*N-2][i][1])*fac[N-i])%mod*C[i][M]%mod; Ans+=pw*W,Ans%=mod;pw*=-1; Ans=((Ans%mod)+mod)%mod; } printf("%lld\n",Ans); //for(int i=0;i<=N;i++) printf("%lld ",(f[2*N-2][i][0]+f[2*N-2][i][1])*fac[N-i]);printf("\n"); }
[x] CF436F
题目求的是对于每个 $c$ ,求 $\max\{\sum [b_i\geq c]\cdot w\cdot c+\sum [b_i<c][a_i\geq p] p\}$ 。
可以发现前一段是定值,而后一段是要求维护一个前缀加等差数列,求最大值的数据结构。
而普通的 $log$ 数据结构难以维护这个类似凸包的东西,考虑分块。
我们对于块维护凸包,如何处理整块我们记录当前整块被覆盖多少次,设为 $cnt$ ,$d_i$ 表示散块对 $i$ 的贡献。
设 $i<j$ ,若比较 $d_i+i\cdot cnt$ 与 $d_j+j\cdot cnt$ 的大小其实比的就是 $\dfrac{d_i-d_j}{j-i}$ 与 $cnt$ 的大小,那么维护一个上凸壳。
而对于散块我们可以暴力重构当前块,时间复杂度 $O(n\sqrt{n})$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; const int MAXM=451; vector<int> vec[MAXN]; int N,A[MAXN],B[MAXN],W,Id[MAXN],U; int Q[MAXM][MAXM],head[MAXM],tail[MAXM],Cnt[MAXN],d[MAXN],Maxn,Ans; bool cmp(int id1,int id2,int id3){return (d[id1]-d[id2])*(id3-id2)<=(d[id2]-d[id3])*(id2-id1);} void calc(int id,int S){while(head[id]<tail[id]&&(d[Q[id][head[id]]]-d[Q[id][head[id]+1]])<=S*(Q[id][head[id]+1]-Q[id][head[id]])) head[id]++;return;} void rebuild(int id){ head[id]=1,tail[id]=0; int l=(id-1)*U+1,r=min(100000ll,id*U); for(int i=l;i<=r;i++){ while(head[id]<tail[id]&&!cmp(Q[id][tail[id]-1],Q[id][tail[id]],i)) tail[id]--; Q[id][++tail[id]]=i; } calc(id,0); return; } void Add(int u){ if(!u) return; //cerr<<u<<"-haha"<<endl; for(int i=1;i<Id[u];i++) Cnt[i]++,calc(i,Cnt[i]); int l=(Id[u]-1)*U+1,r=min(Id[u]*U,100000ll); for(int i=l;i<=r;i++) d[i]+=Cnt[Id[u]]*i; for(int i=l;i<=u;i++) d[i]+=i; Cnt[Id[u]]=0; rebuild(Id[u]); return; } signed main(){ //freopen("1.in","r",stdin); N=read(),W=read(); for(int i=1;i<=N;i++) A[i]=read(),B[i]=read(),Maxn=max(Maxn,B[i]); U=sqrt(100000); for(int i=1;i<=100000;i++) Id[i]=(i-1)/U+1; for(int i=1;i<=Id[100000];i++) rebuild(i); for(int i=1;i<=N;i++) vec[B[i]].pb(i); int cnt=N; for(int i=0;i<=Maxn+1;i++){ if(i){for(auto v:vec[i-1]) Add(A[v]),cnt--;} int Ans1=cnt*W*i,Ans2=-1,ps=-1; for(int j=1;j<=Id[100000];j++){int x=Q[j][head[j]];if(d[x]+Cnt[j]*x>=Ans2)Ans2=d[x]+Cnt[j]*x,ps=x;} //cerr<<"Ans2:"<<Ans2<<" ps:"<<ps<<endl; Ans=Ans1+Ans2; printf("%lld %lld\n",Ans1+Ans2,ps); //if(i==2) return 0; }return 0; }
[x] CF449E
小学奥数题。
设 $f(i)$ 表示四个点可以划分到一个 $i\cdot i$ 的矩形内的包含小方格的总个数。
我们令 $n\leq m$ 则 $Ans=\sum_{i=1}^n(n-i+1)\cdot (m-i+1)\cdot f(i)$ 。
而
$$
f(n)=n^2+\sum_{i=1}^{n-1} (n-2\cdot i)^2+4\cdot g(i,n-i)
$$
$g(i,j)$ 表示在一个 $i,j$ 为底的直角三角形中包含的小方格个数。
由于皮克定理可得
$$
g(i,j)=\dfrac{nm-n-m+\gcd(n,m)}{2}
$$
通过化简式子那么可以快速求得 $f(i)$ 。
则现在只要快速求
$$
\sum_i\sum_j (n-i+1)\cdot (m-i+1)\cdot f_i
$$
设 $A_{i,j}$ 表示 $n=i,m=j$ 时的答案。
可以发现, $A_{i,i}$ 与 $A_{i-1,i-1}$ 是有递推关系的,$A_{i,i}$ 与 $A_{i,j}$ 也是有递推关系的,则我们只要处理 $\sum f_i$ 与 $\sum f_i\cdot i$ 。 (但其实只要再将原式化简就好了
时间复杂度 $O(n\log n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define int long long #define mod 1000000007 #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; const int inv2=500000004; const int inv6=166666668; const int Lim=1e6; int N,M,F[MAXN],W[MAXN],v[MAXN],pri[MAXN],phi[MAXN]; int ksm(int a,int b){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;} int gcd(int a,int b){return !b?a:gcd(b,a%b);} int g(int a,int b){return (a*b-a-b+gcd(a,b))/2;} int Q1(int a){return a*(a+1)%mod*inv2%mod;} int Q2(int a){return a*(a+1)%mod*(2*a+1)%mod*inv6%mod;} void Init(){ phi[1]=1; for(int i=2;i<=Lim;i++){ if(!v[i]){v[i]=i;pri[++pri[0]]=i;phi[i]=i-1;} for(int j=1;pri[j]*i<=Lim&&j<=pri[0];j++){ v[pri[j]*i]=pri[j]; if(!(i%pri[j])){phi[i*pri[j]]=phi[i]*pri[j];break;} phi[pri[j]*i]=phi[i]*(pri[j]-1); } }return; } int G[MAXN],S1[MAXN],S2[MAXN]; signed main(){ //freopen("5.in","r",stdin); Init(); for(int i=1;i<=Lim;i++){ for(int j=1;i*j<=Lim;j++) W[i*j]+=phi[i]*j%mod,W[i*j]%=mod; W[i]-=i,W[i]=((W[i]%mod)+mod)%mod; } int cas=read(); for(int i=1;i<=Lim;i++){ int res=0; res=i*i*i; res%=mod; res-=2*i*Q1(i-1);res%=mod; res+=2*Q2(i-1); res%=mod; res-=2*(i-1)*i; res%=mod;res+=2*W[i],res%=mod; res=(res%mod+mod)%mod; F[i]=res; } for(int i=1;i<=Lim;i++){ G[i]=G[i-1]+F[i]+(2*i+1)*S1[i-1]-2*S2[i-1]; G[i]=((G[i]%mod)+mod)%mod; S1[i]=(S1[i-1]+F[i])%mod,S2[i]=(S2[i-1]+F[i]*i)%mod; } while(cas--){ N=read(),M=read(); if(N>M) swap(N,M); /*int Ans=0; for(int i=1;i<=N;i++) Ans+=(N-i+1)*(M-i+1)%mod*F[i]%mod,Ans%=mod;*/ int del=M-N,Ans=G[N],res=0; res=del*((N+1)*S1[N]%mod-S2[N])%mod,res=((res%mod)+mod)%mod; printf("%lld\n",(Ans+res)%mod); } }
20210110
清华集训 $2014\space day1$ 。
[x] 玛里苟斯
可以发现当 $k\geq 3$ 时我们的基变量不会很大,因为我们设基变量有 $k$ 个,则下界为 $\dfrac{\sum_{i=0}^{2^k-1} i^k}{2^{k-1}}$ ,估一估可以发现基变量不会超过 $23$ 个。
则我们只要建出线性基以后暴力求解。
若 $k=1$ 时,由于 $\sum_{i=0}^n [2\mid i]\dbinom{n}{i}=\sum_{i=0}^n [2\nmid i] \dbinom{n}{i}=2^{n-1}$ ,故选与不选是等价的,简单计算。
若 $k=2$ 时,我们由于算的是 $i,j$ ,考虑沿用上述做法那么除 $4$ ,但是有可能 $i,j$ 有可能在线性基中有同意线性表达,那么我们只能将其看成整体,则其实除 $2$ 即可。
以上做法套用 $int128$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int __int128 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,A[MAXN],K; void print(int x){ if(!x) return; print(x/10); putchar(x%10+'0'); } namespace s1{ void Main(){ int res=0; double Ans=0; for(int i=1;i<=N;i++) res|=A[i]; print(res/2); if(res&1) printf(".5");printf("\n"); exit(0); } } namespace s2{ int p[63],Sta[63],RR; void ins(int x){ for(int i=62;i>=0;i--){ if(!(x&(1ull<<i))) continue; if(!p[i]){p[i]=x;break;} x^=p[i]; }return; } int Find(int x){ int EE=0; for(int i=62;i>=0;i--) if(p[i]&(1ull<<x)) EE|=(1ull<<i); return EE; } void Main(){ int Ans=0; for(int i=1;i<=N;i++) ins(A[i]),RR|=A[i]; for(int i=62;i>=0;i--) Sta[i]=Find(i); for(int i=0;i<=62;i++) for(int j=0;j<=62;j++){ if(((1ull<<i)&RR)&&((1ull<<j)&RR)){ Ans+=(1ull<<i)*(1ull<<j)*(1<<(Sta[i]==Sta[j])); } } Ans/=2; print(Ans/2); if(Ans%2) printf(".5\n"); printf("\n"); exit(0); } } namespace s3{ int p[63],P[63]; double Ans; void ins(int x){ for(int i=62;i>=0;i--){ if(!(x&(1ull<<i))) continue; if(!p[i]){p[i]=x;break;} x^=p[i]; }return; } void Main(){ for(int i=1;i<=N;i++) ins(A[i]); int Ans=0; for(int i=0;i<=62;i++) if(p[i]) P[++P[0]]=p[i]; for(int i=0;i<(1<<P[0]);i++){ int res=0; for(int j=1;j<=P[0];j++) if(i&(1<<(j-1))) res^=P[j]; int w=res; res=1; for(int j=1;j<=K;j++) res*=w; Ans+=res; } int s=(1<<P[0]); print(Ans/s); if((Ans%s)) printf(".5"); printf("\n"); exit(0); } } namespace Force{ void Main(){ double Ans=0; for(int i=0;i<(1<<N);i++){ int res=0; for(int j=1;j<=N;j++) if((1<<(j-1))&i) res^=A[j]; int w=res; res=1;for(int j=1;j<=K;j++) res*=w; Ans+=(double)res/(1<<N); } } } signed main(){ //freopen("10.in","r",stdin); bool ff=1; N=read(),K=read(); for(int i=1;i<=N;i++) A[i]=read(),ff&=(A[i]==0); if(ff){printf("0\n");return 0;} if(K==1) s1::Main(); if(K==2) s2::Main(); s3::Main(); }/* 3 1 1 2 3 */
[x] 主旋律
补集转化,考虑当前图缩点后为 $DAG$ 的情况。
考虑一个问题,给你一个一般图,求有多少个边子集使得图为 $DAG$ 。
设 $f_{S}$ 表示 $S$ 的导出子图为 $DAG$ 的方案数,$Cnt_{X,Y}$ 表示入边在 $X$ 集合,出边在 $Y$ 集合的边的个数。
我们钦定 $T$ 为出度为 $0$ 的点的集合,可以发现在 $S-T$ 中有可能存在入度为 $0$ 的点,故考虑容斥。
$$
f_S=\sum_{T\in S,T\ne \varnothing} (-1)^{|T|-1}\cdot f_{S-T}\cdot 2^{Cnt_{S-T,T}}
$$
考虑有缩点的情况。
可以发现对上述式子我们枚举的 $T$ 是缩完点的情况,则我们少算到的是 $T$ 的每个强连通分量内部连边方案。
设 $F_S$ 表示 $S$ 的导出子图为的强连通图的方案数,$G_S,H_S$ 表示分别表示点集 $S$ 的导出子图中的边集的个数,使得图中形成奇/偶数个强联通分量,并且这些强联通分量互相之间没有边。
$$
F_S=2^{Cnt_{S,S}}-\sum_{T\in S,T\ne \varnothing} (G_T-H_T)\cdot 2^{Cnt_{S-T,S-T}+Cnt_{S-T,T}}
$$
而 $G_S=\sum \prod_{k\space is \space odd} F_{p_i}$ ,$p_i$ 表示一个集合使得该集合为强连通分量,且 $\cap p_i=S$ 且 $p_i$ 无交。同理可得 $H_S$ 。
故 $G,H,F$ 可以互相转移。
时间复杂度 $O(n\cdot 3^n)$ ,发现无法通过该题,于是使用 $bitset$ 优化求 $Cnt$ 过程。
时间复杂度 $O(\dfrac{m3^n}{w})$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<bitset> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=16; int N,M,F[1<<MAXN],G[1<<MAXN],H[1<<MAXN],Num[1<<MAXN],pw[MAXN*MAXN]; bitset<MAXN*MAXN> In[1<<MAXN],Out[1<<MAXN]; int lowbit(int x){return x&-x;} int Qcnt(int in,int out){return (In[in]&Out[out]).count();} vector<int> vec0[MAXN],vec1[MAXN]; signed main(){ //freopen("7.in","r",stdin); N=read(),M=read(); for(int i=1;i<=M;i++){int u=read()-1,v=read()-1;In[1<<u][i]=1,Out[1<<v][i]=1;} pw[0]=1; for(int i=1;i<=M;i++) pw[i]=pw[i-1]*2%mod; for(int i=1;i<=(1<<N)-1;i++){ int x=lowbit(i); if(x!=i) In[i]=In[i-x]|In[x],Out[i]=Out[i-x]|Out[x]; Num[i]=Num[i>>1]+(i&1); } F[0]=1,G[0]=0,H[0]=1; for(int i=1;i<=(1<<N)-1;i++){ F[i]=pw[Qcnt(i,i)]; for(int j=(i-1)&i;j;j=(j-1)&i){ F[i]-=(G[j]-H[j]%mod+mod)%mod*pw[Qcnt(i-j,i-j)]%mod*pw[Qcnt(i-j,j)]%mod; F[i]%=mod; if(!(j&lowbit(i))) continue; G[i]+=H[i-j]*F[j]; G[i]%=mod; H[i]+=G[i-j]*F[j]; H[i]%=mod; } F[i]-=(G[i]-H[i]);F[i]=(F[i]%mod+mod)%mod; G[i]+=F[i],G[i]%=mod; } printf("%lld\n",F[(1<<N)-1]); return 0; }/* 2 1 1 2 */
[] 奇数国
线段树维护。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<bitset> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define LL long long #define mod 19961993 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; const int Lim=1e6; int v[MAXN],pri[MAXN],Q,Id[MAXN],inv[MAXN]; void Init(){ for(int i=2;i<=Lim;i++){ if(!v[i]){v[i]=i;pri[++pri[0]]=i;} for(int j=1;j<=pri[0]&&i<=Lim/pri[j];j++){ v[i*pri[j]]=pri[j]; if(!(i%pri[j])) break; } }return; } bitset<61> Ans1; int Ans2; struct Segment{ bitset<61> A[MAXN<<2]; int mul[MAXN<<2]; void pushup(int k){A[k]=(A[k<<1]|A[k<<1|1]);mul[k]=(LL)mul[k<<1]*mul[k<<1|1]%mod;return;} void build(int k,int l,int r){ if(l==r){A[k][2]=1;mul[k]=3;return;} int mid=(l+r)>>1; build(k<<1,l,mid),build(k<<1|1,mid+1,r); pushup(k); return; } void Modify(int k,int l,int r,int ps,int w){ if(l==r){ A[k].reset(); int s=w; mul[k]=w; while(s!=1){A[k][Id[v[s]]]=1;s/=v[s];} return; } int mid=(l+r)>>1; if(ps<=mid) Modify(k<<1,l,mid,ps,w); if(mid<ps) Modify(k<<1|1,mid+1,r,ps,w); pushup(k); return; return; } void Query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y){Ans1|=A[k],Ans2=(LL)Ans2*mul[k]%mod;return;} int mid=(l+r)>>1; if(x<=mid) Query(k<<1,l,mid,x,y); if(mid<y) Query(k<<1|1,mid+1,r,x,y); } }S; signed main(){ //freopen("5.in","r",stdin); Init(); for(int i=1;i<=60;i++) Id[pri[i]]=i; inv[1]=1; for(int i=2;i<=281;i++) inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod; S.build(1,1,Lim); Q=read(); while(Q--){ int opt=read(); if(opt==1){ int ps=read(),w=read(); S.Modify(1,1,Lim,ps,w); continue; } Ans1.reset(),Ans2=1; int l=read(),r=read(); S.Query(1,1,Lim,l,r); for(int j=1;j<=60;j++) if(Ans1[j]){int v=pri[j];Ans2=(LL)Ans2*(v-1)%mod*inv[v]%mod;} printf("%d\n",Ans2); }return 0; }/* 6 0 1 3 1 1 5 0 1 3 1 1 7 0 1 3 0 2 3 */
20210111
[] CF444E
答案由于具有单调性考虑二分,则现在的问题变为给每个点找个匹配使得最后满足 $x$ 的要求。
猜一下若每个点都符合条件则肯定能找出匹配。证明考虑我们把图建成二分图要求为完美匹配,利用 $HALL$ 定理判定可以发现单个的肯定强于多个的,故得证。
发现这个东西其实不用二分直接排序计算。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e3+11; int N,X[MAXN],sum; struct Edge{int u,v,w;} E[MAXN]; bool cmp(Edge x1,Edge x2){return x1.w<x2.w;} struct Union{ int f[MAXN],s[MAXN],siz[MAXN]; void init(){for(int i=1;i<=N;i++) s[i]=X[i],f[i]=i,siz[i]=1;return;} int find(int x){return f[x]==x?x:f[x]=find(f[x]);} void merge(int x,int y){ int t1=find(x),t2=find(y); if(t1==t2) return; f[t2]=t1,s[t1]+=s[t2]; siz[t1]+=siz[t2]; } }U; int main(){ //freopen("3.in","r",stdin); N=read(); for(int i=1;i<N;i++) E[i].u=read(),E[i].v=read(),E[i].w=read(); for(int i=1;i<=N;i++) X[i]=read(),sum+=X[i]; U.init(); sort(E+1,E+N,cmp); for(int i=1;i<N;i++){ int u=E[i].u,v=E[i].v,w=E[i].w; U.merge(u,v); int rt=U.find(u); int Siz=U.siz[rt],All=U.s[rt]; //for(int j=1;j<=N;j++) printf("%d ",U.s[j]);printf("\n"); //for(int j=1;j<=N;j++) printf("%d ",U.siz[j]); printf("\n"); if(Siz>sum-All){printf("%d\n",E[i].w);return 0;} }printf("0\n"); return 0; }/* 4 1 2 1 2 3 2 3 4 3 1 1 1 1 */
[x] CF603D
根据西姆森定理,该定理表示为过三角形外接圆上异于三角形顶点的任意一点作三边或其延长线上的垂线,则三垂足共线。
故我们将任意一点看成原点判断是否三点共线,简单高中数学计算。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<cmath> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e3+11; int N,Ans; double X[MAXN],Y[MAXN],tmp[MAXN]; int calc(int x){return (x*(x-1))/2;} bool same(double s1,double s2){return fabs(s1-s2)<=1e-12;} int main(){ //freopen("3.in","r",stdin); N=read(); for(int i=1;i<=N;i++){double a,b,c; cin>>a>>b>>c;X[i]=(a*c)/(a*a+b*b),Y[i]=(b*c)/(a*a+b*b);} for(int i=1;i<=N;i++){ int cc=0,cnt=0,S=0; for(int j=i+1;j<=N;j++){ //cerr<<X[i]<<" "<<X[j]<<" "<<Y[i]<<" "<<Y[j]<<endl; if(same(X[i],X[j])&&same(Y[i],Y[j])) ++S; else if(same(X[i],X[j])) cnt++; else tmp[++cc]=(Y[j]-Y[i])/(X[j]-X[i]); } //cerr<<"cnt:"<<cnt<<" "<<S<<endl; Ans+=calc(cnt); Ans+=S*(N-i-1); sort(tmp+1,tmp+cc+1); int l=1; for(int j=2;j<=cc;j++) if(!same(tmp[j],tmp[l])){Ans+=calc(j-l);l=j;} Ans+=calc(cc-l+1); } printf("%d\n",Ans); return 0; }/* 4 1 0 0 0 1 0 1 1 -1 1 -1 2 3 0 1 1 1 1 2 1 -1 -2 */
[] CF773E
假设 $\forall i,A_i>0$ ,保证 $A$ 排序,且初始时 $F=0$ ,我们如何计算最后的 $F$ 。
设 $F_i$ 表示通过 $i$ 这段区间的 $F$ ,$cnt_i$ 表示 $A$ 序列中有多少个 $i$ 。
则 $F_i=\min\{F_{i-1}+cnt_i,i\}$。 等价于 $F_i=\min\{F_{i-1},i-cnt_i\}+cnt_i$ 。
推导可得 $F_n=\sum_{i=0}^n cnt_i+\min_i\{i-\sum_{j=0}^i cnt_j\}$ 。
考虑一开始 $F_0$ 如果是负数,那么答案可以写成 $F_n=\sum_{i=0}^n cnt_i+\min\{\min_i\{i-\sum_{j=0}^i cnt_j\},F_0\}$ 。
这个算法为啥要保证 $A_i>0$ ,其实我们只要保证 $F\leq A_1$ ,即无 $3$ 操作的情况。
考虑有 $3$ 操作,即为 $\exists i,A_i<0$ 的情况,那么我们肯定能找到一个阀值使得后面均无 $3$ 操作。
最后一个 $3$ 操作出现的地方一定是满足单调性的,即找到 $A_i\geq -i$ ,然后二分。然后套用上述做法。
时间复杂度 $O(n\log^2 n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; const int L=-5e5; const int R=5e5; struct Segment1{ int Minn[MAXN<<2],tag[MAXN<<2]; void build(int k,int l,int r){ if(l==r){Minn[k]=l;return;} int mid=(l+r)>>1; build(k<<1,l,mid),build(k<<1|1,mid+1,r); Minn[k]=min(Minn[k<<1],Minn[k<<1|1]);return; } void pushdown(int k){ if(!tag[k]) return; Minn[k<<1]+=tag[k],Minn[k<<1|1]+=tag[k]; tag[k<<1]+=tag[k],tag[k<<1|1]+=tag[k]; tag[k]=0;return; } void Modify(int k,int l,int r,int x,int y,int w){ if(x<=l&&r<=y){tag[k]+=w,Minn[k]+=w;return;} int mid=(l+r)>>1; pushdown(k); if(x<=mid) Modify(k<<1,l,mid,x,y,w); if(mid<y) Modify(k<<1|1,mid+1,r,x,y,w); Minn[k]=min(Minn[k<<1],Minn[k<<1|1]); return; } int Query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return Minn[k]; int mid=(l+r)>>1,res=INT_MAX; pushdown(k); if(x<=mid) res=min(res,Query(k<<1,l,mid,x,y)); if(mid<y) res=min(res,Query(k<<1|1,mid+1,r,x,y)); Minn[k]=min(Minn[k<<1],Minn[k<<1|1]); return res; } }S; struct Segment2{ int Sum[MAXN<<2]; void Modify(int k,int l,int r,int ps,int w){ if(l==r){Sum[k]+=w;return;} int mid=(l+r)>>1; if(ps<=mid) Modify(k<<1,l,mid,ps,w); if(mid<ps) Modify(k<<1|1,mid+1,r,ps,w); Sum[k]=Sum[k<<1]+Sum[k<<1|1]; return; } int Query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return Sum[k]; int res=0,mid=(l+r)>>1; if(x<=mid) res+=Query(k<<1,l,mid,x,y); if(mid<y) res+=Query(k<<1|1,mid+1,r,x,y); return res; } int Find(int k,int l,int r,int kth){ if(l==r) return l; int mid=(l+r)>>1; if(Sum[k<<1]<kth) return Find(k<<1|1,mid+1,r,kth-Sum[k<<1]); return Find(k<<1,l,mid,kth); } }T; int Q,all,Minn=INT_MAX; void ins(int x){S.Modify(1,L,R,x,R,-1);T.Modify(1,L,R,x,1);all++;Minn=min(Minn,x);return;} int Query(int ps,int F){ int cnt=T.Query(1,L,R,ps,R),Minn=min(F,S.Query(1,L,R,ps,R)+(all-cnt)); return cnt+Minn; } int Sta[MAXN]; int main(){ //freopen("maker.in","r",stdin); S.build(1,L,R); Q=read(); while(Q--){ int x=read(); ins(x); if(Minn>=0){printf("%d\n",Query(0,0));continue;} int l=1,r=all,res=INT_MAX; while(l<=r){ int mid=(l+r)>>1; if(T.Find(1,L,R,mid)<=-mid) res=mid,l=mid+1; else r=mid-1; } if(res==INT_MAX||res==all){printf("%d\n",-all);continue;} int Kth=T.Find(1,L,R,res+1); printf("%d\n",Query(Kth,-res)); } } /* 3 0 2 -1 */
20210113
[x] CF198D
[] CF446D
我们将陷阱点看成关键点,则若我们处理出 $p_{i,j}$ 表示从 $i$ 有多少的概率从到 $j$ 即可解决问题,因为后面的直接跑矩阵快速幂。
对于暴力做法来说我们枚举 $i$ ,可以在 $O(n^3)$ 的复杂度内得到 $p_{i,j}$ ,总复杂度为 $O(n^4)$ 过不去。
我们发现除 $i$ 点有一个 $1$ 以外对于 $\forall i$ ,所有其他系数是相同的。故在高斯消元中维护系数矩阵。本质上相当于求矩阵逆,但没编出来意义(
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=501; const int MAXS=101; double A[MAXN][MAXN],F[MAXN][MAXN],K; vector<int> vec[MAXN]; int N,M,d[MAXN],col[MAXN],SS; double P[MAXN][MAXN]; struct Matrix{double W[MAXS][MAXS]; void Init(){memset(W,0,sizeof(W));return;}}FF,GG; Matrix operator*(Matrix x1,Matrix x2){ Matrix x3;x3.Init(); for(int i=1;i<=SS;i++) for(int j=1;j<=SS;j++) for(int k=1;k<=SS;k++) x3.W[i][j]+=x1.W[i][k]*x2.W[k][j]; return x3; } Matrix ksm(Matrix a,int b){ Matrix ans=a; b--; while(b){if(b&1) ans=ans*a; a=a*a,b>>=1;} return ans; } int Id[MAXN],id[MAXN]; int main(){ //freopen("11.in","r",stdin); N=read(),M=read(),K=read(); for(int i=1;i<=N;i++) col[i]=read(); for(int i=1;i<=M;i++){ int u=read(),v=read(); d[u]++,d[v]++; vec[u].pb(v),vec[v].pb(u); } for(int i=1;i<=N;i++){ F[i][i]=A[i][i]=1; if(col[i]) continue; for(auto v:vec[i]) A[v][i]-=(double)((double)1/d[i]); } /*for(int i=1;i<=N;i++){for(int j=1;j<=N;j++) cout<<A[i][j]<<" ";printf("\n");} printf("===========\n");*/ for(int i=1;i<=N;i++){ int ps=-1; for(int j=i;j<=N;j++) if(A[j][i]){ps=j;break;} for(int j=1;j<=N;j++) swap(A[i][j],A[ps][j]),swap(F[i][j],F[ps][j]); for(int j=1;j<=N;j++){ if(i==j||!A[j][i]) continue; double res=A[j][i]/A[i][i]; for(int k=1;k<=N;k++) A[j][k]-=A[i][k]*res,F[j][k]-=F[i][k]*res; } } for(int i=1;i<=N;i++){ for(int j=1;j<=N;j++){ P[i][j]=F[j][i]/A[j][j]; } } for(int i=1;i<=N;i++) if(col[i]) Id[++Id[0]]=i,id[i]=Id[0]; for(int i=1;i<=Id[0];i++){ for(int j=1;j<=Id[0];j++){ double res=0; for(auto v:vec[Id[i]]){ if(col[v]&&v==Id[j]) res+=(double)1/d[Id[i]]; if(!col[v]) res+=((double)1/d[Id[i]])*P[v][Id[j]]; } P[Id[i]][Id[j]]=res; } } if(K==2){printf("%.10lf\n",P[1][N]);return 0;} SS=Id[0]; for(int i=1;i<=SS;i++) for(int j=1;j<=SS;j++) FF.W[i][j]=P[Id[i]][Id[j]]; //for(int i=1;i<=SS;i++){for(int j=1;j<=SS;j++) cout<<FF.W[i][j]<<" ";printf("\n");} GG=ksm(FF,K-2); //for(int i=1;i<=SS;i++){for(int j=1;j<=SS;j++) cout<<GG.W[i][j]<<" ";printf("\n");} double Ans=0; for(int i=1;i<=N;i++) if(col[i]) Ans+=P[1][i]*GG.W[id[i]][Id[0]]; printf("%.10lf\n",Ans); return 0; }/* 2 1 3 0 1 1 2 */
[x] CF494E
根据翻硬币游戏这种博弈方式,我们可以每次看成局面的 $sg$ 值为仅看一个白色的 $sg$ 值异或起来。
设 $sg_{i,j}$ 为 $(i,jj)$ 为白色,其余均为黑色的 $sg$ 值。可得 $sg_{i,j}=mex\{Xor_{len<k\and len<\min (i,j)} \space sg_{i-len,j-len}\}$ 。
打表发现 $sg_{i,j}=\min\{lowbit(i),lowbit(j),p\}$ ,其中 $p$ 代表 $\max p,2^p\leq k$ 。
那么我们只要维护 $sg_{i,j}=2^t$ 有多少个。
考虑这个问题的平面的问题,可以发现 $[l,r]$ 中 $lowbit$ 为 $i$ 的为 $(\dfrac{r}{2^i}-\dfrac{l-1}{2^i})-(\dfrac{r}{2^{i+1}}-\dfrac{l-1}{2^{i+1}})$ ,而做法为求 $\geq i$ 与 $\geq i+1$ 。
考虑套用此做法,那么问题变为矩形交,扫描线维护。时间复杂度 $O(n\log^2 n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int tmp[MAXN],N,M,K,Lim; struct Segment{ int Sum[MAXN<<2],tag[MAXN<<2]; void Init(){memset(Sum,0,sizeof(Sum)),memset(tag,0,sizeof(tag));return;} void pushup(int k,int l,int r){ if(tag[k]) Sum[k]=tmp[r]-tmp[l-1]; else if(l==r){Sum[k]=0;return;} else Sum[k]=Sum[k<<1]+Sum[k<<1|1]; return; } void Add(int k,int l,int r,int x,int y,int w){ if(x<=l&&r<=y){tag[k]+=w;pushup(k,l,r);return;} int mid=(l+r)>>1; if(x<=mid) Add(k<<1,l,mid,x,y,w); if(mid<y) Add(k<<1|1,mid+1,r,x,y,w); pushup(k,l,r); return; } }S; int X1[MAXN],X2[MAXN],Y1[MAXN],Y2[MAXN]; int Num[MAXN],tot; struct node{int L,R,ps,w;} E[MAXN]; bool cmp(node x1,node x2){return x1.ps<x2.ps;} void ins(int x1,int y1,int x2,int y2){ if(x1>x2||y1>y2) return; tmp[++tmp[0]]=x1-1,tmp[++tmp[0]]=x2; ++tot;E[tot].L=x1,E[tot].R=x2,E[tot].ps=y1,E[tot].w=1; ++tot;E[tot].L=x1,E[tot].R=x2,E[tot].ps=y2+1,E[tot].w=-1; return; } int calc(int r){ tot=0; tmp[0]=0; S.Init(); int all=0; for(int i=1;i<=M;i++) ins((X1[i]-1)/(1<<r)+1,(Y1[i]-1)/(1<<r)+1,X2[i]/(1<<r),Y2[i]/(1<<r)); sort(tmp+1,tmp+tmp[0]+1); sort(E+1,E+tot+1,cmp); Lim=unique(tmp+1,tmp+tmp[0]+1)-tmp-1; for(int i=1;i<=tot;i++) E[i].L=lower_bound(tmp+1,tmp+Lim+1,E[i].L)-tmp,E[i].R=lower_bound(tmp+1,tmp+Lim+1,E[i].R)-tmp; tmp[0]=0; for(int i=1,j=1;i<=tot;i=j){ all+=(E[i].ps-E[i-1].ps)*S.Sum[1]; while(E[i].ps==E[j].ps&&j<=tot) S.Add(1,1,Lim,E[j].L,E[j].R,E[j].w),++j; } return all; } signed main(){ //freopen("3.in","r",stdin); N=read(),M=read(),K=read(); for(int i=1;i<=M;i++) X1[i]=read(),Y1[i]=read(),X2[i]=read(),Y2[i]=read(); int res=0; while((1<<(res+1))<=K) res++; for(int i=0;i<=res;i++) Num[i]=calc(i); //for(int i=0;i<=res;i++) printf("%lld ",Num[i]);printf("\n"); for(int i=0;i<=res;i++){ if((Num[i]-Num[i+1])&1){printf("Hamed\n");return 0;} } printf("Malek\n"); return 0; }/* 5 2 1 1 1 3 3 2 2 4 4 */
20210104
[] CF103E
考虑对于求出一个 $p$ 的排列使得 $p_i\in A_i$ ,可以发现我们肯定能找出一组 $p$ ,因为题面上的条件说的就是 $Hall$ 定理。
可以才想如果我们选择 $i$ 集合那么我们对于里面任意一个元素都要在 $p'$ 中选择,$p'$ 表示 $p$ 的反映射。
考虑反证法。我们假设选定 $v_1,v_2,...,v_k$ ,则他们的并集至少含有 $\{p_{v_1},p_{v_2},...,p_{v_k}\}$,则如果我们在 $v_1$ 中有一个点没有在 $v_2,v_3,...,v_k$ 的 $p$ 中出现过,那么并集的元素个数肯定 $\geq k+1$ ,得证。
而逆定理显然是对的,那么我们可以将集合按照 $(i,j)$ 表示选择 $i$ 就必须选 $j$ 连边,跑最大权闭合子图即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=611; const int MAXM=311*311; struct node{int u,v,w,nex;}E[MAXM<<1]; int head[MAXN],dis[MAXN],S,T,A,B,N,cnt,M,INF=INT_MAX; queue<int> que; void add(int u,int v,int w){ E[cnt].u=u,E[cnt].v=v,E[cnt].w=w,E[cnt].nex=head[u],head[u]=cnt++; swap(u,v),w=0; E[cnt].u=u,E[cnt].v=v,E[cnt].w=w,E[cnt].nex=head[u],head[u]=cnt++; return; } bool bfs(){ while(!que.empty()) que.pop(); memset(dis,127/3,sizeof(dis)); int inf=dis[0]; dis[S]=0; que.push(S); while(!que.empty()){ int xx=que.front(); que.pop(); for(int i=head[xx];i!=-1;i=E[i].nex){ int v=E[i].v,w=E[i].w; if(dis[v]>dis[xx]+1&&w){ dis[v]=dis[xx]+1; que.push(v); } } }return dis[T]!=inf; } int dfs(int u,int flow){ if(u==T) return flow; int used=0; for(int i=head[u];i!=-1;i=E[i].nex){ int v=E[i].v,w=E[i].w; if(dis[v]==dis[u]+1&&w){ int slow=dfs(v,min(flow-used,E[i].w)); used+=slow; E[i].w-=slow,E[i^1].w+=slow; if(used==flow) break; } }if(!used) dis[u]=-1; return used; } int dinic(){int Ans=0; while(bfs()) Ans+=dfs(S,INT_MAX); return Ans;} void Init(){memset(head,-1,sizeof(head)),cnt=0;return;} int pre[MAXN],Pre[MAXN],W[MAXN],Ans; vector<int> vec[MAXN]; signed main(){ //freopen("3.in","r",stdin); Init(); N=read(); S=0,T=2*N+1; for(int i=1;i<=N;i++){ int x=read(); add(S,i,1),add(i+N,T,1); for(int j=1;j<=x;j++){int s=read();vec[i].pb(s);add(i,s+N,INF);} } dinic(); for(int i=1;i<=N;i++){ for(int j=head[i];j!=-1;j=E[j].nex){ int v=E[j].v,w=E[j].w; if(v>N&&v<=2*N&&w!=INF){pre[i]=v-N,Pre[v-N]=i;} } } //for(int i=1;i<=N;i++) printf("%d ",pre[i]);printf("\n"); Init(); T=N+1; for(int i=1;i<=N;i++) for(auto v:vec[i]) if(Pre[v]!=i) add(i,Pre[v],INF); for(int i=1;i<=N;i++) W[i]=read(); for(int i=1;i<=N;i++){ if(W[i]<=0) add(S,i,-W[i]),Ans+=(-W[i]); else add(i,T,W[i]); } printf("%d\n",dinic()-Ans); return 0; }/* 3 1 1 2 2 3 1 3 10 20 -3 */
[] CF273E
由于为平等博弈考虑 $sg$ 函数。可以发现我们对于 $(l,r)$ 其实我们只要关心 $r-l$ 就行。
设 $sg(x)$ 表示当 $r-l=x$ 时的 $sg$ 值,则 $sg(x)=mex\{sg(\lfloor \frac{x}{3}\rfloor)\,sg(x-\lfloor \frac{x}{3}\rfloor))\}$ 。
打表发现虽然没有规律但是连续段很少,则我们只要打表连续段即可。由于多状态游戏等于所有单一状态的异或值,$dp$ 计算。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e3+11; const int inv2=(mod+1)/2; struct pii {int L,R,v;}F[]= { (pii){1,2,0}, (pii){3,3,1}, (pii){4,4,2}, (pii){5,6,1}, (pii){7,8,2}, (pii){9,12,0}, (pii){13,14,1}, (pii){15,18,2}, (pii){19,26,0}, (pii){27,38,1}, (pii){39,39,2}, (pii){40,56,0}, (pii){57,57,2}, (pii){58,80,1}, (pii){81,84,2}, (pii){85,119,0}, (pii){120,120,2}, (pii){121,173,1}, (pii){174,178,2}, (pii){179,254,0}, (pii){255,259,2}, (pii){260,362,1}, (pii){363,381,2}, (pii){382,536,0}, (pii){537,543,2}, (pii){544,779,1}, (pii){780,804,2}, (pii){805,1145,0}, (pii){1146,1168,2}, (pii){1169,1631,1}, (pii){1632,1717,2}, (pii){1718,2414,0}, (pii){2415,2446,2}, (pii){2447,3506,1}, (pii){3507,3621,2}, (pii){3622,5153,0}, (pii){5154,5259,2}, (pii){5260,7340,1}, (pii){7341,7729,2}, (pii){7730,10865,0}, (pii){10866,11010,2}, (pii){11011,15779,1}, (pii){15780,16297,2}, (pii){16298,23189,0}, (pii){23190,23668,2}, (pii){23669,33032,1}, (pii){33033,34783,2}, (pii){34784,48893,0}, (pii){48894,49548,2}, (pii){49549,71006,1}, (pii){71007,73339,2}, (pii){73340,104351,0}, (pii){104352,106509,2}, (pii){106510,148646,1}, (pii){148647,156526,2}, (pii){156527,220019,0}, (pii){220020,222969,2}, (pii){222970,319529,1}, (pii){319530,330028,2}, (pii){330029,469580,0}, (pii){469581,479293,2}, (pii){479294,668909,1}, (pii){668910,704370,2}, (pii){704371,990086,0}, (pii){990087,1003363,2}, (pii){1003364,1437881,1}, (pii){1437882,1485129,2}, (pii){1485130,2113112,0}, (pii){2113113,2156821,2}, (pii){2156822,3010091,1}, (pii){3010092,3169668,2}, (pii){3169669,4455389,0}, (pii){4455390,4515136,2}, (pii){4515137,6470465,1}, (pii){6470466,6683083,2}, (pii){6683084,9509006,0}, (pii){9509007,9705697,2}, (pii){9705698,13545410,1}, (pii){13545411,14263509,2}, (pii){14263510,20049251,0}, (pii){20049252,20318115,2}, (pii){20318116,29117093,1}, (pii){29117094,30073876,2}, (pii){30073877,42790529,0}, (pii){42790530,43675639,2}, (pii){43675640,60954347,1}, (pii){60954348,64185793,2}, (pii){64185794,90221630,0}, (pii){90221631,91431520,2}, (pii){91431521,131026919,1}, (pii){131026920,135332445,2}, (pii){135332446,192557381,0}, (pii){192557382,196540378,2}, (pii){196540379,274294562,1}, (pii){274294563,288836071,2}, (pii){288836072,405997337,0}, (pii){405997338,411441843,2}, (pii){411441844,589621136,1}, (pii){589621137,608996005,2}, (pii){608996006,866508215,0}, (pii){866508216,884431704,2}, (pii){884431705,1000000000,1} };//102 int N,P; int Query(int l,int r){ return (((r-l+1)*P%mod-(l+r)*(r-l+1)%mod*inv2%mod)%mod+mod)%mod; } int f[3],G[MAXN][4]; signed main(){ //freopen("2.in","r",stdin); N=read(),P=read(); for(int i=0;i<=101;i++){ int L=F[i].L,R=F[i].R,opt=F[i].v; R=min(R,P); if(L>R) break; if(!opt) f[0]+=Query(L,R); if(opt==1) f[1]+=Query(L,R); if(opt==2) f[2]+=Query(L,R); f[0]%=mod,f[1]%=mod,f[2]%=mod; } //cerr<<f[0]<<" "<<f[1]<<" "<<f[2]<<" "<<f[0]+f[1]+f[2]<<endl; G[0][0]=1; for(int i=0;i<N;i++){ for(int j=0;j<=3;j++){ for(int k=0;k<=2;k++) G[i+1][j^k]+=G[i][j]*f[k]%mod,G[i+1][j^k]%=mod; } } printf("%lld\n",(G[N][1]+G[N][2]+G[N][3])%mod); return 0; }
[x] CF736D
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<bitset> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e3+11; const int MAXM=5e5+11; bitset<MAXN*2> A[MAXN]; int N,M,U[MAXM],V[MAXM]; void print(){printf("==========\n");for(int i=1;i<=N;i++){for(int j=1;j<=2*N;j++) cout<<A[i][j]<<" ";printf("\n");}printf("===========\n");} void Gauss(){ for(int i=1;i<=N;i++){ int ps=0; for(int j=i;j<=N;j++) if(A[j][i]){ps=j;break;} swap(A[i],A[ps]); for(int j=1;j<=N;j++){ if(!A[j][i]||i==j) continue; A[j]^=A[i]; } }return; } int main(){ //freopen("2.in","r",stdin); N=read(),M=read(); for(int i=1;i<=M;i++){int u=read(),v=read();U[i]=u,V[i]=v;A[u][v]=1;} for(int i=1;i<=N;i++) A[i][i+N]=1; Gauss(); for(int i=1;i<=M;i++) printf(A[V[i]][U[i]+N]?"NO\n":"YES\n"); return 0; }