题目链接 https://vjudge.net/contest/240074#overview
只写一下自己做的几个题吧
/* D n^2的暴力dp怎么搞都可以的 这里先预处理 i到j的串时候合法 转移的时候枚举上一个状态 O1判断 */ #include<cstdio> #include<cstring> #include<iostream> #define maxn 1010 using namespace std; int T,n,m,f[maxn],g[maxn][maxn]; char s[maxn]; int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); scanf("%s",s+1); int p,ok; memset(g,0,sizeof(g)); for(int i=1;i<=n;i++){ ok=0;p=1; for(int j=i+1;j<=n;j++){ if(s[j]==s[i])p++; else p--; if(s[j]==s[j-1])ok=1; if((j-i+1)&1){ if(p==1&&ok==0)g[i][j]=1; } else{ if(p==0&&ok==0)g[i][j]=1; } } } memset(f,127/3,sizeof(f));f[0]=0; for(int i=1;i<=n;i++){ for(int j=i-m;j<i;j++) if(j>=0&&g[j+1][i]==0) f[i]=min(f[i],f[j]+1); } printf("%d ",f[n]-1); } return 0; } /* n小的很 直接暴力枚举选不选的每种情况 在此之前 从大到小排一遍序 就可以避免把司机当成智障的情况了 */ #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int T,n,s,a[11],ans; int cmp(int x,int y){ return x>y; } void Dfs(int now,int sum,int cnt){ if(sum>=s){ ans=max(ans,cnt);return; } if(now==n+1){ if(sum>=s)ans=max(ans,cnt); return; } Dfs(now+1,sum+a[now],cnt+1); Dfs(now+1,sum,cnt); } int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&n,&s); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n,cmp); ans=0;Dfs(1,0,0); printf("%d ",ans); } return 0; } /* 思路应该是找到直径,连成环 emmmmm图的直径不会找.... 那就 先缩点,弄成树 然后找一下直径 缩完点之后边数就是桥的个数 */ #include<cstdio> #include<iostream> #include<cstring> #include<queue> #define maxn 200010 using namespace std; int T,n,m,num,head[maxn],dfn[maxn],low[maxn]; int s[maxn],top,f[maxn],topt,bl[maxn]; int Head[maxn],N; queue<int>q; struct node{ int v,pre; }e[maxn],p[maxn]; int init(){ int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } void Memset(){ memset(head,0,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(s,0,sizeof(s)); memset(f,0,sizeof(f)); memset(bl,0,sizeof(bl)); memset(Head,0,sizeof(Head)); N=0;num=0;top=0;topt=0;N=0; } void Add(int from,int to){ num++;e[num].v=to; e[num].pre=head[from]; head[from]=num; } void Ad(int from,int to){ num++;p[num].v=to; p[num].pre=Head[from]; Head[from]=num; } void Tarjan(int u,int from){ dfn[u]=low[u]=++topt; s[++top]=u;f[u]=1; for(int i=head[u];i;i=e[i].pre){ int v=e[i].v; if(v==from)continue; if(dfn[v]==0){ Tarjan(v,u);low[u]=min(low[u],low[v]); } else if(f[v])low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]){ N++;while(s[top]!=u){ f[s[top]]=0;bl[s[top]]=N;top--; } f[s[top]]=0;bl[s[top]]=N;top--; } } int Bfs(int S){ memset(f,0,sizeof(f)); memset(s,0,sizeof(s)); f[S]=1;q.push(S); while(!q.empty()){ int k=q.front();q.pop(); for(int i=Head[k];i;i=p[i].pre){ int v=p[i].v; if(f[v])continue; f[v]=1;q.push(v);s[v]=s[k]+1; } } int mx=0,V=S; for(int i=1;i<=N;i++) if(mx<s[i]){ mx=s[i];V=i; } return V; } int Bf(int S){ memset(f,0,sizeof(f)); memset(s,0,sizeof(s)); f[S]=1;q.push(S); while(!q.empty()){ int k=q.front();q.pop(); for(int i=Head[k];i;i=p[i].pre){ int v=p[i].v; if(f[v])continue; f[v]=1;q.push(v);s[v]=s[k]+1; } } int mx=0; for(int i=1;i<=N;i++) mx=max(mx,s[i]); return mx; } int main(){ T=init(); while(T--){ n=init();m=init();int u,v; Memset(); for(int i=1;i<=m;i++){ u=init();v=init(); Add(u,v);Add(v,u); } for(int i=1;i<=n;i++) if(dfn[i]==0)Tarjan(i,0); num=0;for(u=1;u<=n;u++) for(int i=head[u];i;i=e[i].pre){ v=e[i].v; if(bl[u]==bl[v])continue; Ad(bl[u],bl[v]); } int S=Bfs(1); printf("%d ",num/2-Bf(S)); } return 0; } /* I 一开始一直不知道从那下手 只是想到某些点应该固定了 只改一次值 但是并没有找到是那些点 好吧挺简单的就是第一个点 (寄几太弱) 如果我们确定了最后的数都输 X 并且操作的区间长度是K 那么第一个数只可能通过操作 1-k这个区间来搞 同理往后推 就找到了下一个只改一次的点 在以这个点为起点往后重复上述操作 最后检查剩下的点是不X就好啦 然后算一下 T*10*N*N*N 哎呀 GG 一开始想能不能把枚举K的那个N二分优化成log 然后发现不单调 这就很明显了 优化的是最后那个n*n 一开始无脑套线段树T成智障 第二天看题解有这么一句 : some contestants used segment tree to do it and got tle..... delete 再见 考虑每次往后修改k个数 每个+C的过程慢了 我们维护一个sum 代表累积到这里需要加几 然后这个区间完事之后剪掉 搞成On 优雅 */ #include<cstdio> #include<cstring> using namespace std; int T,n,k,sub[260]; char s[260],ss[260]; int main(){ scanf("%d",&T); while(T--){ scanf("%s",ss+1); n=strlen(ss+1);int ok=0; for(k=n;k>=1;k--){ for(char fir='0';fir<='9';fir++){ for(int i=1;i<=n;i++)s[i]=ss[i]; memset(sub,0,sizeof(sub)); int falg=0,sum=0,c; for(int i=1;i+k-1<=n;i++){ sum-=sub[i]; s[i]=(s[i]-'0'+sum)%10+'0'; c=(fir-s[i]+10)%10; sum+=c;s[i]=fir;sub[i+k]+=c; } for(int i=n-k+2;i<=n;i++){ sum-=sub[i]; s[i]=(s[i]-'0'+sum)%10+'0'; } for(int i=1;i<n;i++) if(s[i]!=s[i+1]){ falg=1;break; } if(falg==0){ ok=1;printf("%d ",k);break; } } if(ok)break; } } return 0; } /* D题的加强版 需要优化 按照D题的思路 对于每个i 在i-k - i-1 中选一个最优的且合法 的状态转移过来 最优这件事可以用数据结构优化 但是是不是合法 就要在研究研究 我们到这思考 想要这个题可做 那i的上一个状态一定是从一段连续 的区间里选 我们这次转移新构造的串是 j-i 这一块 而题目不合法的串是010101这样子的 倘若出现了00 或 11 后面不论加什么(长度合法)都是合法的串串 这就很巧妙地迎合了我们的设想 i-k 011010100101010 i-1 i 下面我们考虑 维护隔得i最近的还是最远的 00 11 (这里一开始脑子GG了 wa到挺) 我们选择上一个状态的左区间一定是i-k 最优的情况下 肯定是 右区间越靠右越好 那我们就找 离得i最近的00 11 这个东西预处理一下 存在dis里面 然后我这个代码写的有点丑了 左边界老是取不好 我就直接把1-k的区间预处理了一下 */ #include<cstdio> #include<iostream> #define maxn 400010 #define lc k*2 #define rc k*2+1 #define mid (l+r)/2 using namespace std; int T,n,k,dis[maxn],f[maxn],s[maxn]; char S[maxn]; void Insert(int k,int l,int r,int x,int y){ if(l==x&&r==x){ s[k]=y;return; } if(x<=mid)Insert(lc,l,mid,x,y); else Insert(rc,mid+1,r,x,y); s[k]=min(s[lc],s[rc]); } int Query(int k,int l,int r,int x,int y){ if(x<=l&&y>=r)return s[k]; int ret=1e9; if(x<=mid)ret=min(ret,Query(lc,l,mid,x,y)); if(y>mid)ret=min(ret,Query(rc,mid+1,r,x,y)); return ret; } int main(){ scanf("%d",&T); while(T--){ scanf("%d%d%s",&n,&k,S+1); for(int i=0;i<=n*4;i++){ s[i]=1e9;f[i]=1e9; } int pos=0; for(int i=2;i<=n;i++){ if(S[i]==S[i-1])pos=i-2; dis[i]=pos; } int falg=0;f[1]=1; Insert(1,1,n,1,1); for(int i=2;i<=k;i++){ if(S[i]==S[i-1])falg=1; if(falg)f[i]=1; else f[i]=i; Insert(1,1,n,i,f[i]); } for(int i=k+1;i<=n;i++){ if(dis[i]<i-k)f[i]=f[i-1]+1; else f[i]=Query(1,1,n,i-k,dis[i])+1; Insert(1,1,n,i,f[i]); } printf("%d ",f[n]-1); } return 0; }