模拟赛
串(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;
}
来源:https://www.cnblogs.com/thmyl/p/8177534.html
