湖南雅礼培训 1.2
模拟赛
串(string)
【题目描述】
给定一个由小写字母组成的字符串 s,每次你可以删去它的一个非回文子串,
求删成空串的最小次数。
【输入数据】
第一行一个整数 t 表示数据组数。
每组数据第一行一个整数 n 表示字符串长度,第二行一个字符串 s。
【输出数据】
每组数据输出一行一个整数表示答案,如果无法删成空串输出-1。
【样例输入】
2
7
abcdcba
3
xxx
【样例输出】
2
-1
【样例解释】
对于第一个样例,一种最优方案为 abcdcba->adcba->空串。
【数据范围】
对于 30%的数据,n<=10。
对于 60%的数据,n<=100。
对于 100%的数据,t<=20,n<=10^5。
#include<iostream> #include<cstdio> #include<cstring> #define maxn 110 #define INF 0x7fffffff using namespace std; int ans; struct node{ string s; int len; }; void solve(node now,int step){//已经进行了step次 if(step>=ans)return; for(int l=0;l<now.len;l++){ for(int r=l+1;r<now.len;r++){ int ll=l,rr=r;bool flag=0; while(ll<=rr){ if(now.s[ll]!=now.s[rr]){flag=1;break;} else ll++,rr--; } if(flag==0)continue; node nxt;nxt.s="";nxt.len=0; for(int i=0;i<l;i++)nxt.s+=now.s[i],nxt.len++; for(int i=r+1;i<now.len;i++)nxt.s+=now.s[i],nxt.len++; if(nxt.len==0){ ans=min(ans,step+1); return; } solve(nxt,step+1); } } } int main(){ // freopen("Cola.txt","r",stdin); freopen("string.in","r",stdin);freopen("string.out","w",stdout); int T; scanf("%d",&T); while(T--){ ans=INF; node str; scanf("%d",&str.len);cin>>str.s; solve(str,0); if(ans<INF)printf("%d\n",ans); else puts("-1"); } return 0; }
/* 首先如果s不是回文串答案为1。 考虑对于1<=i<n,是否都满足s[1..i]和s[i+1..n]至少有一个是回文的。如果不满足那么答案为2。 如果满足的话,如果s[1]=s[2],那么s只会形如aaaaa或aaabaaa;如果s[1]!=s[2],那么s只会形如abababa。这三种情况都是无解的。 */ #include<iostream> #include<cstring> #include<cstdio> using namespace std; int n; char s[100010]; int main(){ freopen("string10.in","r",stdin); int T; scanf("%d",&T); while(T--){ scanf("%d%s",&n,s+1); int l=1,r=n; bool flag=0; while(l<=r){ if(s[l]!=s[r]){flag=1;break;}//不是回文串 l++,r--; } if(flag){puts("1");continue;} for(int i=1;i<=n;i++){//枚举中间点 l=1,r=i; bool flag1=0,flag2=0; while(l<=r){ if(s[l]!=s[r]){flag1=1;break;} l++,r--; } if(flag1==0)continue; l=i+1,r=n; while(l<=r){ if(s[l]!=s[r]){flag2=1;break;} l++;r--; } if(flag1&&flag2){flag=1;puts("2");break;} } if(!flag)puts("-1"); } return 0; }
/* 不是直接求得回文串 而是根据下面的三种情况得到的 如果不是下面三种情况 就肯定是满足条件的 如果满足的话,如果s[1]=s[2],那么s只会形如aaaaa或aaabaaa;如果s[1]!=s[2],那么s只会形如abababa。这三种情况都是无解的。 也就是其它情况都是输出2 */ #include<iostream> #include<cstring> #include<cstdio> using namespace std; int n; char s[100010]; int main(){ freopen("string9.in","r",stdin); int T; scanf("%d",&T); while(T--){ scanf("%d%s",&n,s+1); int l=1,r=n; bool flag=0; while(l<=r){ if(s[l]!=s[r]){flag=1;break;}//不是回文串 l++,r--; } if(flag){puts("1");continue;} bool f1=0,f2=0,f3=0; for(int i=1;i<=n;i++){ if(i>1&&s[i]!=s[i-1])f1=1;//不满足第一个条件 if(i>2&&s[i]!=s[i-2])f2=1;//不满足第二个条件 } int mid=(n/2)+1; for(int i=2;i<mid;i++)if(s[i]!=s[i-1])f3=1; for(int i=mid+2;i<=n;i++)if(s[i]!=s[i-1])f3=1; if(f1&&f2&&f3)puts("2");//三个条件都不满足 else puts("-1"); } return 0; }
变量(variable)
【题目描述】
有 n 个变量 w[1]~w[n],每个变量可以取 W 或-W。
有 p 个式子,形如 Hi=ai|w[xi]-w[yi]|+bi|w[yi]-w[zi]|+ci|w[zi]-w[xi]|
+di(w[xi]-w[yi])+ei(w[yi]-w[zi])+fi(w[zi]-w[xi])。
有 q 个条件,形如 w[x]<=w[y]或 w[x]=w[y]或 w[x]<w[y]。
最小化 sigma(wi)+sigma(Hi)。
【输入数据】
第一行一个整数 t 表示数据组数。
每组数据第一行四个整数 n,W,p,q 表示节点数。
接下来 p 行每行九个整数 xi,yi,zi,ai,bi,ci,di,ei,fi。
接下来 q 行每行三个整数 x,y,r。
r=0 表示 w[x]<=w[y];r=1 表示 w[x]=w[y];r=2 表示 w[x]<w[y]。
保证存在方案。
【输出数据】
每组数据输出一行一个整数表示 sigma(wi)+sigma(Hi)的最小值。
【样例输入】
1
3 1 1 1
1 2 3 1 1 1 1 1 1
1 2 2
【样例输出】
3
【数据范围】
对于 30%的数据,n<=15,p,q<=20。
对于 100%的数据,t<=10,n<=500,p,q<=1000,1<=W<=10^6,
0<=ai,bi,ci,di,ei,fi<=1000。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #define maxn 510 #define INF 0x7fffffff using namespace std; int ans,n,W,p,q,link[maxn][maxn],w[maxn]; struct node{ int x,y,z,a,b,c,d,e,f; }qu[maxn]; struct Node{ int x,y,v; }r[maxn]; bool check(int sta){ for(int i=1;i<=n;i++){ w[i]=sta&(1<<(i-1)); if(w[i]!=0)w[i]=W; else w[i]=-W; } for(int i=1;i<=q;i++){ if(r[i].v==0){ if(w[r[i].x]>w[r[i].y])return 0; } else if(r[i].v==1){ if(w[r[i].x]!=w[r[i].y])return 0; } else if(r[i].v==2){ if(w[r[i].x]>=w[r[i].y])return 0; } } return 1; } int main(){ // freopen("Cola.txt","r",stdin); freopen("variable.in","r",stdin);freopen("variable.out","w",stdout); int T; scanf("%d",&T); while(T--){ ans=INF; scanf("%d%d%d%d",&n,&W,&p,&q); for(int i=1;i<=p;i++)scanf("%d%d%d%d%d%d%d%d%d",&qu[i].x,&qu[i].y,&qu[i].z,&qu[i].a,&qu[i].b,&qu[i].c,&qu[i].d,&qu[i].e,&qu[i].f); for(int i=1;i<=q;i++)scanf("%d%d%d",&r[i].x,&r[i].y,&r[i].v); for(int sta=0;sta<(1<<n);sta++){ if(!check(sta))continue; int s1=0,s2=0; for(int i=1;i<=n;i++)s1+=w[i]; for(int i=1;i<=p;i++){ s2+=qu[i].a*abs(w[qu[i].x]-w[qu[i].y])+qu[i].b*abs(w[qu[i].y]-w[qu[i].z])+qu[i].c*abs(w[qu[i].z]-w[qu[i].x]); s2+=qu[i].d*(w[qu[i].x]-w[qu[i].y])+qu[i].e*(w[qu[i].y]-w[qu[i].z])+qu[i].f*(w[qu[i].z]-w[qu[i].x]); } ans=min(ans,s1+s2); } printf("%d\n",ans); } return 0; }
/* 每个变量建一个点i,S向i连边,i向T连边。割这两条边分别表示取-W和取W。对于x[i]*w[i],讨论正负往其中一条边上加权值。 对于y[j]*|w[a[j]]-w[b[j]]|,在a[j]和b[j]间连权值为2y[j]的无向边。 对于限制,wa<wb等价于wa=-W且wb=W;wa=wb就在a和b间连权值为正无穷的无向边;wa<=wb就a向b连权值为正无穷的单向边。最后求最小割即可。 时间复杂度O(t*maxflow(n,n+p+q)) */ #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<queue> #define INF 0x3f3f3f3f using namespace std; int n,m,s,t,op,oq,head[10005],num=1,dis[10005],vis[10005],mp[510][510],val[510],cur[10005]; long long w; struct node{ int to,pre,cap,flow; }e[100005]; long long qread(){ int j=1;long long i=0; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')j=-1;ch=getchar();} while(ch<='9'&&ch>='0'){i=i*10+ch-'0';ch=getchar();} return i*j; } void Insert(int from,int to,int v){ e[++num].to=to;e[num].cap=v;e[num].flow=0;e[num].pre=head[from];head[from]=num; e[++num].to=from;e[num].cap=0;e[num].flow=0;e[num].pre=head[to];head[to]=num; } int bfs(){ for(int i=1;i<=t;i++)dis[i]=INF,vis[i]=0,cur[i]=head[i]; queue<int>q;q.push(s);dis[s]=0;vis[s]=1;q.push(s); while(!q.empty()){ int now=q.front();q.pop(); for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(!vis[to]&&e[i].cap-e[i].flow>0){ vis[to]=1;dis[to]=dis[now]+1;q.push(to); } } } return vis[t]; } int dfs(int now,int flow){ if(now==t||flow==0)return flow; int delta,rest=0; for(int &i=cur[now];i;i=e[i].pre){ int to=e[i].to; if(dis[now]+1==dis[to]){ delta=dfs(to,min(flow,e[i].cap-e[i].flow)); if(delta>0){ e[i].flow+=delta; e[i^1].flow-=delta; rest+=delta; flow-=delta; if(flow==0)break; } } } return rest; } int dinic(){ int maxflow=0; while(bfs())maxflow+=dfs(s,INF); return maxflow; } int main(){ freopen("variable9.in","r",stdin); int T;scanf("%d",&T); while(T--){ scanf("%d",&n);w=qread();scanf("%d%d",&op,&oq); memset(head,0,sizeof(head));num=1; memset(mp,0,sizeof(mp)); memset(val,0,sizeof(val)); for(int i=1;i<=n;i++)val[i]=1; for(int i=1;i<=op;i++){ int xi,yi,zi,ai,bi,ci,di,ei,fi; scanf("%d%d%d%d%d%d%d%d%d",&xi,&yi,&zi,&ai,&bi,&ci,&di,&ei,&fi); val[xi]+=di-fi; val[yi]+=ei-di; val[zi]+=fi-ei; if(xi<yi)mp[xi][yi]+=ai;else mp[yi][xi]+=ai; if(yi<zi)mp[yi][zi]+=bi;else mp[zi][yi]+=bi; if(zi<xi)mp[zi][xi]+=ci;else mp[xi][zi]+=ci; } int mx=0; for(int i=1;i<=n;i++)mx=max(mx,abs(val[i])); mx++; s=2*n+1;t=2*n+2; for(int i=1;i<=n;i++){ Insert(s,i,mx+val[i]); Insert(i,i+n,INF); Insert(i+n,t,mx-val[i]); } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){ if(mp[i][j]==0)continue; Insert(i+n,j,2*mp[i][j]); Insert(j+n,i,2*mp[i][j]); } for(int i=1;i<=oq;i++){ int xi,yi,zi; scanf("%d%d%d",&xi,&yi,&zi); if(zi==0)Insert(yi+n,xi,INF); if(zi==1)Insert(xi+n,yi,INF),Insert(yi+n,xi,INF); if(zi==2)Insert(s,xi,INF),Insert(yi+n,t,INF); } long long ans=1LL*(dinic()-mx*n)*w; cout<<ans<<endl; } return 0; }
取石子(stone)
【题目描述】
有 n 堆石子,第 i 堆有 xi 个。
Alice 和 Bob 轮流取石子(先后手未定),Alice 每次从一堆中取走 a 个,Bob
每次从一堆中取走 b 个,无法操作者输。
不难发现只会有四种情况:Alice 必胜;Bob 必胜;先手必胜;后手必胜。
你需要选定若干堆石子(共有 2^n 种方案),Alice 和 Bob 只能在你选出的堆
中取,问以上四种情况对应的方案数。
【输入数据】
第一行三个整数 n,a,b,第二行 n 个整数 x1~xn。
【输出数据】
一行四个整数,分别表示 Alice 必胜、Bob 必胜、先手必胜和后手必胜的方
案数,对 10^9+7 取模。
【样例输入】
2 2 3
2 3
【样例输出】
2 0 1 1
【样例解释】
选定空集时后手必胜,选定{2}时 Alice 必胜,选定{3}时先手必胜,选定{2,3}时 Alice
必胜。
【数据范围】
对于 10%的数据,n,xi<=5。
对于 50%的数据,n<=20。
对于另外 10%的数据,a=b。
对于又另外 20%的数据,a=1。
对于 100%的数据,1<=n<=100000,1<=a,b,xi<=10^9。
/* 不妨假设a<b。 每堆石子先对a+b取模,然后可以分为4种: (1) xi<a,没用。 (2) a<=xi<b,只要存在则a必胜。 (3) b<=xi<2a,只和奇偶性有关。 (4) 2a<=xi,存在至少2个则a必胜,存在1个且(3)为偶数则先手必胜,存在1个且(3)为奇数则a必胜,不存在且(3)为奇数则先手必胜,不存在且(3)为偶数则后手必胜。 时间复杂度O(n) */ #include<bits/stdc++.h> #define L long long using namespace std; const int q=1000000007; int n,a,b,x[4],f[4]; inline int power(int a,int b) { if(!b) return 1; int c=power(a,b>>1); c=(L)c*c%q; if(b&1) c=(L)c*a%q; return c; } int main() { freopen("stone.in","r",stdin); freopen("stone.out","w",stdout); int i,j,k=0; scanf("%d%d%d",&n,&a,&b); if(a>b) swap(a,b),k=1; for(i=1;i<=n;i++) { scanf("%d",&j); j%=a+b; x[(j>=a)+(j>=b)+(j>=b && j>=2*a)]++; } f[0]=((L)(power(2,x[1])-1)*power(2,x[2]+x[3])+(L)(power(2,x[3])-x[3]-1+q)*power(2,x[2])+(L)x[3]*(x[2]?power(2,x[2]-1):0))%q; f[2]=((x[2]?power(2,x[2]-1):0)+(L)x[3]*(x[2]?power(2,x[2]-1):1))%q; f[3]=(x[2]?power(2,x[2]-1):1); for(i=0;i<4;i++) f[i]=(L)f[i]*power(2,x[0])%q; if(k) swap(f[0],f[1]); for(i=0;i<4;i++) printf("%d ",f[i]); printf("\n"); return 0; }