屯题1
题目屯着,满了50道写一波题解
分5篇写,每篇10道这样
1.UOJ118 赴京赶考
考虑a[i]!=a[i+1],那么无论哪一行,这两个相邻的走过去都需要1的代价
同样的b[i]!=b[i+1],那么无论哪一列,这两个相邻的走过去都需要1的代价
所以(x,y)走到(xx,yy)等价于x走到xx的最小代价(1维情况即可)
+y走到yy的最小代价(也是1维情况)
维护一个前缀和就好了
代码如下
#include<bits/stdc++.h> #define N 500005 using namespace std; int n,m,Q,xc,xs,yc,ys,a[N],b[N],sumh[N],suml[N]; int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=m;i++) scanf("%d",&b[i]); for (int i=2;i<=n;i++) sumh[i]=sumh[i-1]+(a[i]!=a[i-1]); for (int i=2;i<=m;i++) suml[i]=suml[i-1]+(b[i]!=b[i-1]); scanf("%d",&Q); while (Q--){ scanf("%d%d%d%d",&xs,&ys,&xc,&yc); if (xs>xc) swap(xs,xc); if (ys>yc) swap(ys,yc); int tmp=sumh[xc]-sumh[xs]; tmp=min(tmp,sumh[n]-sumh[xc]+(a[1]!=a[n])+sumh[xs]); int ans=tmp; tmp=suml[yc]-suml[ys]; tmp=min(tmp,suml[m]-suml[yc]+(b[1]!=b[m])+suml[ys]); printf("%d\n",tmp+ans); } return 0; }
2.UOJ48
把a[1]的所有质因数求出来
我们发现次大公约数=最大公约数/共有的最小质因数
代码如下:
#include<bits/stdc++.h> #define int long long #define N 500005 using namespace std; int n,cnt,a[N],num[N]; int gcd(int x,int y){if(!y)return x;return gcd(y,x%y);} signed main(){ scanf("%lld",&n); for (int i=1;i<=n;i++) scanf("%lld",&a[i]); int xb=a[1];cnt=0; for (int i=2;i*i<=xb;i++){ if (a[1]%i) continue; while (a[1]%i==0) a[1]/=i; num[++cnt]=i; } if (a[1]!=1) num[++cnt]=a[1]; a[1]=xb; for (int i=1;i<=n;i++){ int G=gcd(a[i],xb);int j; if(G==1){printf("-1 ");continue;} for (j=1;j<=cnt;j++){ if (G%num[j]) continue; else break; } printf("%lld ",G/num[j]); } return 0; }
3.UOJ31
思维题
考虑一个右括号,它找到离它最近的,并且没有被选定的左括号交换
因为都是n个所以一定能找到
那么这么交换一定能保证最后序列合法
左括号被选定的位置是单调的
所以O(n)扫一遍就好了
#include<bits/stdc++.h> #define N 500005 using namespace std; struct node{int l,r;}a[N]; int n,cnt;char s[N]; int main(){ scanf("%s",s+1);n=strlen(s+1); int now=1,last=0; for (int i=1;i<=n;i++) if (s[i]=='(') last=max(last,i); for (int i=1;i<=n;i++){ if (s[i]==')'){ now=max(now,i); if (now>last) break; a[++cnt].l=i; for (;now<=n;now++){ if (s[now]=='('){ a[cnt].r=now; now++; break; } } } } printf("%d\n",cnt); for (int i=1;i<=cnt;i++) printf("%d %d\n",a[i].l,a[i].r); return 0; }
4.UOJ30
圆方树好题
我们考虑每一个方点只维护它所有儿子的信息
这样的话修改就是logn的了
查询的时候,考虑(u,v),如果u和v的lca是方点
那么再算一下lca的父亲的贡献
如果是圆点那么一定考虑完全了,因为方点的父亲一定被包括算进去了
代码如下:
#include<bits/stdc++.h> #define N 2000005 using namespace std; int sz[N],head[N],heavyson[N],top[N],dfn[N],low[N],d[N];char ss[5]; int rel[N],id[N],father[N],w[N],s[N],dep[N],n,m,Q,x,y,topp,kk,tim1,cnt; vector<int>G[N]; multiset<int>S[N]; struct Edge{int nxt,to;}e[N]; inline void link(int x,int y){e[++kk].nxt=head[x];e[kk].to=y;head[x]=kk;} void tarjan(int u,int fa){ dfn[u]=low[u]=++tim1;s[++topp]=u; for (int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if (v==fa) continue; if (!dfn[v]){ tarjan(v,u); low[u]=min(low[u],low[v]); if (low[v]>=dfn[u]){//说明u是割点,v所在的点双到u为止,u以上的都不能在这个点双内 cnt++;G[u].push_back(cnt); while (s[topp]!=v){ G[cnt].push_back(s[topp]); S[cnt].insert(w[s[topp]]); topp--; } G[cnt].push_back(s[topp]); S[cnt].insert(w[s[topp]]); topp--; } } else low[u]=min(low[u],dfn[v]); } } void dfs1(int u,int fa){ sz[u]=1; for (int i=0;i<(int)G[u].size();i++){ int v=G[u][i]; if (v==fa) continue; dep[v]=dep[u]+1; father[v]=u; dfs1(v,u); sz[u]+=sz[v]; if (!heavyson[u]||sz[v]>sz[heavyson[u]]) heavyson[u]=v; } } void dfs2(int u,int first){ top[u]=first;id[u]=++tim1; rel[tim1]=u; if (!heavyson[u]) return; dfs2(heavyson[u],first); for (int i=0;i<(int)G[u].size();i++){ int v=G[u][i]; if (v==father[u]||v==heavyson[u]) continue; dfs2(v,v); } } inline int lca(int x,int y){ while (top[x]!=top[y]){ if (dep[top[x]]<dep[top[y]]) swap(x,y); x=father[top[x]]; } if (dep[x]<dep[y]) return x;return y; } void build(int k,int l,int r){ if (l==r){ int x=rel[l]; if (x>n) d[k]=*S[x].begin(); else d[k]=w[x]; return; } int mid=(l+r)>>1; build(k*2,l,mid);build(k*2+1,mid+1,r); d[k]=min(d[k*2],d[k*2+1]); } void update(int k,int l,int r,int x,int y){ if (l==r){d[k]=y;return;} int mid=(l+r)>>1; if (x<=mid) update(k*2,l,mid,x,y); else update(k*2+1,mid+1,r,x,y); d[k]=min(d[k*2],d[k*2+1]); } void update1(int k,int l,int r,int x,int y,int z){ if (l==r){ int u=rel[l]; S[u].erase(S[u].find(y)); S[u].insert(z); int mn=*S[u].begin(); update(1,1,cnt,id[u],mn); return; } int mid=(l+r)>>1; if (x<=mid) update1(k*2,l,mid,x,y,z); else update1(k*2+1,mid+1,r,x,y,z); d[k]=min(d[k*2],d[k*2+1]); } int getpos(int k,int l,int r,int x){ if (l==r) return d[k]; int mid=(l+r)>>1; if (x<=mid) return getpos(k*2,l,mid,x); else return getpos(k*2+1,mid+1,r,x); } int query(int k,int l,int r,int x,int y){ if (x<=l&&y>=r) return d[k]; int mid=(l+r)>>1; if (y<=mid) return query(k*2,l,mid,x,y); else if (x>mid) return query(k*2+1,mid+1,r,x,y); else return min(query(k*2,l,mid,x,mid),query(k*2+1,mid+1,r,mid+1,y)); } int Query(int x,int y){ int ans=1e9; while (top[x]!=top[y]){ if (dep[top[x]]<dep[top[y]]) swap(x,y); ans=min(ans,query(1,1,cnt,id[top[x]],id[x])); x=father[top[x]]; } if (dep[x]<dep[y]) swap(x,y); ans=min(ans,query(1,1,cnt,id[y],id[x])); return ans; } int main(){ scanf("%d%d%d",&n,&m,&Q); for (int i=1;i<=n;i++) scanf("%d",&w[i]); for (int i=1;i<=m;i++){ scanf("%d%d",&x,&y); link(x,y);link(y,x); } cnt=n;tarjan(1,-1);tim1=0; dfs1(1,-1);dfs2(1,1); build(1,1,cnt); while (Q--){ scanf("%s%d%d",ss+1,&x,&y); if (ss[1]=='C'){ update(1,1,cnt,id[x],y); if (father[x]>n) update1(1,1,cnt,id[father[x]],w[x],y); w[x]=y; } else { int LCA=lca(x,y); int ans=1e9; if (LCA>n&&father[LCA]) ans=getpos(1,1,cnt,id[father[LCA]]); ans=min(ans,Query(x,y)); printf("%d\n",ans); } } return 0; }
5.UOJ22
我们发现Mod有一个性质
x%y后的值%z,如果(y<z)那么不会有任何改变
所以把a从大到小排个序
用dp[i][j]表示前i个,当前值为j是否可行
然后g[i][j]记录方案数即可
#include<bits/stdc++.h> #define Mod 998244353 #define N 5005 using namespace std; int n,X,a[N],g[N][N];bool dp[N][N]; int main(){ scanf("%d%d",&n,&X); for (int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1);reverse(a+1,a+n+1); dp[0][X]=1;g[0][X]=1; for (int i=1;i<=n;i++){ for (int j=0;j<=X;j++){ dp[i][j%a[i]]|=dp[i-1][j]; g[i][j%a[i]]+=g[i-1][j]; g[i][j%a[i]]%=Mod; } if (i==n) break; for (int j=0;j<=X;j++){ dp[i][j]|=dp[i-1][j]; g[i][j]=1ll*(g[i][j]+1ll*g[i-1][j]*(n-i)%Mod)%Mod; } } for (int i=X;~i;i--) if (dp[n][i]){ printf("%d\n",i); printf("%d\n",g[n][i]); return 0; } return 0; }
6.UOJ14
我们发现加入的边权一直是变大的
所以如果之前i,j之间右边,那么这个就是来卖萌的
然后记录一下每条边是否被用
如果被用,删除了就gg了
大概用一个可撤销并查集维护一下就行了
代码如下:
#include<bits/stdc++.h> #define N 500005 #define int long long using namespace std; char sss[15]; int n,m,top,fa[N],sz[N],num[N],sum[N],op1[N],op2[N],op3[N],opt[N],x[N],y[N]; int find(int x){if(x==fa[x])return x;return find(fa[x]);} inline void link(int L,int R,int step){ top++;num[top]=num[top-1];sum[top]=sum[top-1]; op1[top]=op2[top]=op3[top]=0; int fx=find(L);int fy=find(R); if (fx==fy) return; if (sz[fx]<sz[fy]) swap(fx,fy); op1[top]=1;op2[top]=sz[fx];op3[top]=fy; sz[fx]+=sz[fy]; fa[fy]=fx; num[top]++;sum[top]+=step; } inline void del(int tot){ while (tot--){ if (op1[top]){ int fy=op3[top]; int fx=fa[fy]; fa[fy]=fy; sz[fx]=op2[top]; num[top]--; }top--; } } signed main(){ scanf("%lld%lld",&n,&m); for (int i=1;i<=n;i++) fa[i]=i,sz[i]=1; for (int i=1;i<=m;i++){ scanf("%s",sss+1); if (sss[1]=='A') opt[i]=1; else if (sss[1]=='D') opt[i]=2; else opt[i]=3; if (opt[i]==1) scanf("%lld%lld",&x[i],&y[i]); else if (opt[i]==2) scanf("%lld",&x[i]); } for (int i=1;i<=m;i++){ if (opt[i]==1){ link(x[i],y[i],i); if (num[top]==n-1) printf("%lld\n",sum[top]); else puts("0"); } else if (opt[i]==2){ if (opt[i+1]==3){ if (num[top-x[i]]==n-1) printf("%lld\n",sum[top-x[i]]); else puts("0"); } else { del(x[i]); if (num[top]==n-1) printf("%lld\n",sum[top]); else puts("0"); } } else if (opt[i]==3){ if (opt[i-1]==1) del(1); if (num[top]==n-1) printf("%lld\n",sum[top]); else puts("0"); } } return 0; }
7.CF963A
很无聊的等比数列题
代码如下:
#include<bits/stdc++.h> #define N 600005 #define Mod 1000000009 #define int long long using namespace std; int n,a,b,k,a1;char s[N]; inline int ksm(int x,int y){ int ans1=1;while (y){ if (y&1) ans1=1ll*ans1*x%Mod; y>>=1;x=1ll*x*x%Mod; } return ans1; } signed main(){ scanf("%lld%lld%lld%lld",&n,&a,&b,&k); scanf("%s",s);int opt=0; for (int i=0;i<k;i++){ if (s[i]=='-') opt=-1; else opt=1; a1=1ll*(a1+1ll*opt*ksm(a,n-i)*ksm(b,i)%Mod)%Mod; a1=(a1+Mod)%Mod; } int xi=n+1;int num=xi/k; xi=xi-num*k; int tmpa=ksm(a,k*num);int tmpb=ksm(b,k*num); if (tmpa==tmpb){ printf("%lld\n",1ll*a1*num%Mod); return 0; } int ans1=1ll*(1ll*a1*tmpa%Mod-1ll*a1*tmpb%Mod)%Mod; ans1=(ans1+Mod)%Mod; int fm=(tmpa-1ll*ksm(b,k)*ksm(a,k*num-k)%Mod)%Mod; fm=(fm+Mod)%Mod; ans1=1ll*ans1*ksm(fm,Mod-2)%Mod; printf("%lld\n",ans1%Mod); return 0; }
8.CF917A
很无聊的题
代码如下:
#include<bits/stdc++.h> using namespace std; char s[500005];int ans=0; int main(){ scanf("%s",s+1); int n=strlen(s+1); for (int i=1;i<=n;i++){ int zuo=0;int you=0; for (int j=i;j<=n;j++){ if (s[j]=='(') zuo++,you++; if (s[j]==')') zuo--,you--; if (s[j]=='?') zuo--,you++; if (zuo<0&&you>0) zuo+=2; if (you<0) break; if (!zuo) ans++; } } printf("%d\n",ans); return 0; }
9.CF917B
感觉已经相当套路了
sg好多都可以记忆化搜索
把所有状态遍历一遍,然后求mex
代码如下:
#include<bits/stdc++.h> #define N 105 using namespace std; int n,m,x,y,kk,head[N],dp[N][N][27];char ch; struct Edge{int nxt,to;char ch;}e[N*N*2]; inline void link(int x,int y,char z){e[++kk].nxt=head[x];e[kk].to=y;e[kk].ch=z;head[x]=kk;} bool dfs(int x,int y,int now){ if (dp[x][y][now]!=-1) return dp[x][y][now]; for (int i=head[x];i;i=e[i].nxt){ int v=e[i].to; if (e[i].ch-'a'+1>=now&&dfs(y,v,e[i].ch-'a'+1)==0) return dp[x][y][now]=1; } return dp[x][y][now]=0; } int main(){ scanf("%d%d",&n,&m); memset(dp,-1,sizeof(dp)); for (int i=1;i<=m;i++){ scanf("%d%d",&x,&y); ch=getchar(); while (ch<'a'||ch>'z') ch=getchar(); link(x,y,ch); } for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) dp[i][j][0]=dfs(i,j,0); for (int i=1;i<=n;i++){ for (int j=1;j<=n;j++){ if (dp[i][j][0]) putchar('A'); else putchar('B'); }puts(""); } return 0; }
10.APIO2018 T3
显然构造圆方树
定义方点的大小为所在点双的大小
圆点大小为-1
dp一下即可
代码如下:
#include<bits/stdc++.h> #define N 500005 #define int long long using namespace std; int n,m,x,y,kk,ans,cnt,tim1,sum,top,w[N],s[N],head[N],dfn[N],low[N],sz[N]; vector<int>G[N]; struct Edge{int nxt,to;}e[N]; inline void link(int x,int y){e[++kk].nxt=head[x];e[kk].to=y;head[x]=kk;} void tarjan(int u,int fa){ low[u]=dfn[u]=++tim1;s[++top]=u; for (int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if (v==fa) continue; if (!dfn[v]){ tarjan(v,u); low[u]=min(low[u],low[v]); if (low[v]>=dfn[u]){ ++cnt;w[cnt]=1; G[u].push_back(cnt); while (s[top]!=v) w[cnt]++,G[cnt].push_back(s[top--]); G[cnt].push_back(s[top--]);w[cnt]++; } } else low[u]=min(low[u],dfn[v]); } } void dfs(int u,int fa){ sz[u]=(u<=n); for (int i=0;i<(int)G[u].size();i++){ int v=G[u][i]; if (v==fa) continue; dfs(v,u); sz[u]+=sz[v]; } } void dp(int u,int fa){ int now=u<=n; for (int i=0;i<(int)G[u].size();i++){ int v=G[u][i]; if (v==fa) continue; ans=ans+sz[v]*now*w[u]; dp(v,u); now+=sz[v]; } ans=ans+(sum-now)*now*w[u]; } signed main(){ scanf("%lld%lld",&n,&m); for (int i=1;i<=m;i++){ scanf("%lld%lld",&x,&y); link(x,y);link(y,x); } for (int i=1;i<=n;i++) w[i]=-1; cnt=n; for (int i=1;i<=n;i++) if (!dfn[i]){ tarjan(i,-1); dfs(i,-1); sum=sz[i]; dp(i,-1); } printf("%lld\n",ans*2); return 0; }