T1 串
签到题。看完样例就很容易猜到这题答案多半就是\(\{-1,1,2\}\)里面的,然后感性理解理性证明了一通,发现好像的确是这样。
如果串形如\(aaaaaaa,aaaabaaaa,abababababa\),那么直接无解。
如果串不是回文串,就是1。
剩下的全都是2。
T2 变量
不算难的最小割,不过我网络流实在太差,只有在省选前补了一下,所以并没有做出来。
首先把\(w_i,|w_i-w_j|\)的系数全都整理出来。既然每个点只能有两种选择,所以考虑最小割,设连\(S\)表示选正权值。
然后:
- \(a\times w_x\):连\((x,T,a\times w),(S,x,-a\times w)\)。
- \(a|w_x-w_y|\):连\((x,y,2w),(y,x,2w)\)。
- \(w_x=w_y\):连\((x,y,\infty),(y,x,\infty)\)。
- \(w_x
:连\((x,T,\infty),(S,y,\infty)\)。 - \(w_x\le w_y\):连\((x,y,\infty)\)。
注意到只有第一种边有负权,而这种边一定会恰好删去\(n\)个,所以可以每条边加上一个数,最后再减去。
#include
using namespace std;
//clock_t __t=clock();
#define rep(i,x,y) for (int i=(x);i<=(y);++i)
#define drep(i,y,x) for (int i=(y);i>=(x);--i)
#define temp template
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
temp bool chkmax(T &x,T y){return x'9'||ch<'0') t|=(ch=='-'),ch=getchar();
while (ch<='9'&&ch>='0') x=x*10+ch-48,ch=getchar();
x=(t?-x:x);
}
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define pii pair
#define fir first
#define sec second
#define MP make_pair
#define sz 10101
int n;ll W;int p,q;
ll val[sz],A[666][666];
struct hh{int t;ll w;int nxt;}edge[sz<<1];
int head[sz],ecnt=1;
void make_edge(int f,int t,ll w)
{
edge[++ecnt]=(hh){t,w,head[f]};
head[f]=ecnt;
edge[++ecnt]=(hh){f,0,head[t]};
head[t]=ecnt;
}
int S,T;
#define v edge[i].t
int dep[sz];
bool bfs()
{
queueq;
rep(i,1,T) dep[i]=0;
q.push(S);dep[S]=1;
while (!q.empty())
{
int x=q.front();q.pop();
go(x) if (!dep[v]&&edge[i].w) dep[v]=dep[x]+1,q.push(v);
}
return dep[T];
}
ll dfs(int x,ll f)
{
if (x==T) return f;
ll ret=0;
go(x) if (dep[v]==dep[x]+1&&edge[i].w)
{
ll k=dfs(v,min(f,edge[i].w));
f-=k;ret+=k;edge[i].w-=k;edge[i^1].w+=k;
if (!f) break;
}
if (!ret) dep[x]=-1;
return ret;
}
ll dinic(){ll ret=0;while (bfs()) ret+=dfs(S,1e18);return ret;}
void solve()
{
read(n),read(W),read(p),read(q);
while (p--)
{
ll x,y,z,a,b,c,d,e,f;
read(x),read(y),read(z),read(a),read(b),read(c),read(d),read(e),read(f);
A[min(x,y)][max(x,y)]+=a;
A[min(y,z)][max(y,z)]+=b;
A[min(z,x)][max(z,x)]+=c;
val[x]+=d-f;
val[y]+=e-d;
val[z]+=f-e;
}
rep(i,1,n) ++val[i];
ll mn=1e18;rep(i,1,n) chkmin(mn,val[i]),chkmin(mn,-val[i]);
S=n+1,T=n+2;
rep(i,1,n) make_edge(i,T,val[i]-mn),make_edge(S,i,-val[i]-mn);
rep(i,1,n) rep(j,i+1,n) if (A[i][j]) make_edge(i,j,A[i][j]*2),make_edge(j,i,A[i][j]*2);
while (q--)
{
int x,y,r;read(x),read(y),read(r);
if (r==2) make_edge(x,T,1e18),make_edge(S,y,1e18);
else if (r==1) make_edge(x,y,1e18),make_edge(y,x,1e18);
else make_edge(x,y,1e18);
}
ll sum=dinic();
printf("%lld\n",(sum+mn*n)*W);
rep(i,1,n) val[i]=0;
rep(i,1,n) rep(j,1,n) A[i][j]=0;
ecnt=1;rep(i,1,T) head[i]=0;sum=0;
}
int main()
{
file();
int T;read(T);
while (T--) solve();
return 0;
}
T3 取石子
第一步自然是考虑怎样的情况谁会赢。
可以发现,在这个游戏里一定是取石子少的那个人占便宜,因为他可以不停地抢另一个人的石头,而如果出现一堆石子只有他能再取一次但另一个人不能取,那么他就肯定赢了。
依照这一点,可以猜到两个人必然是先轮流取每一堆石子,也就是这堆石子的数量每次减\(A+B\)。所以,一开始可以直接把所有石子数量模一下\(A+B\)。
接下来,设\(s(small)\)表示少的那个人及他每次取的数量,\(b(big)\)同理。
对于一堆石子\(x\):
- 如果\(x
,那么自然谁都取不了,他对游戏胜负没有影响。 - 如果\(s\le x,那么\(s\)可以去别的地方抢\(b\)的,最后再回来拿掉这个,所以这堆石子一旦出现就\(s\)必胜。
- 如果\(b\le x<2s\),那么谁都可以取他且都只能取一次,所以这只是先后手的问题。
- 如果\(2s\le x
,那么就是最麻烦的了。如果\(s\)取了他,那么就变成了第二种情况,\(s\)必胜。所以不管谁先手,都会先去取这种石头堆。如果只有一个,那么得看谁先手。否则,\(b\)无论如何都不可能取完,\(s\)总是要赢的。
说完这些,做法也就明朗了,无非就是分情况二、三、四有几个来分类讨论。
#include
using namespace std;
//clock_t __t=clock();
#define rep(i,x,y) for (int i=(x);i<=(y);++i)
#define drep(i,y,x) for (int i=(y);i>=(x);--i)
#define temp template
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
temp bool chkmax(T &x,T y){return x'9'||ch<'0') t|=(ch=='-'),ch=getchar();
while (ch<='9'&&ch>='0') x=x*10+ch-48,ch=getchar();
x=(t?-x:x);
}
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define pii pair
#define fir first
#define sec second
#define MP make_pair
#define mod 1000000007ll
ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
ll inv(ll x){return ksm(x,mod-2);}
int n,A,B;
int m1,m2,m3,m4; // 废的,S直接赢的,S可能赢的,只有先后手异位的
int a,b;
ll ans1,ans2,ans3,ans4;
void solve()
{
ans1=ksm(2,n-m2)*(ksm(2,m2)-1)%mod; // 有直接赢的存在
if (m3>=2) (ans1+=ksm(2,m1+m4)*(ksm(2,m3)-m3-1+mod)%mod)%=mod; // 有两个以上可能赢的,S直接赢
if (!m4) ans4=ksm(2,m1); // 没有可能赢的而且没有能取的,后手直接赢
else ans3=ans4=ksm(2,m1+m4-1); // 有可以取的,各一半可能
// 以上两行是没有S可能赢的
// 以下两行是恰好一个S可能赢的
if (m3)
{
if (m4) (ans1+=ksm(2,m1+m4-1)*m3%mod)%=mod,(ans3+=ksm(2,m1+m4-1)*m3%mod)%=mod;
else (ans3+=ksm(2,m1)*m3%mod)%=mod;
}
}
int main()
{
file();
read(n),read(A),read(B);a=min(A,B),b=max(A,B);
rep(i,1,n)
{
int x;
read(x),x%=a+b;
if (x=a) ++m3;
else ++m4;
}
solve();
if (A>B) swap(ans1,ans2);
printf("%lld %lld %lld %lld\n",ans1,ans2,ans3,ans4);
return 0;
}