2019.7.20模拟赛

T1 串

签到题。看完样例就很容易猜到这题答案多半就是\(\{-1,1,2\}\)里面的,然后感性理解理性证明了一通,发现好像的确是这样。

如果串形如\(aaaaaaa,aaaabaaaa,abababababa\),那么直接无解。

如果串不是回文串,就是1。

剩下的全都是2。

T2 变量

不算难的最小割,不过我网络流实在太差,只有在省选前补了一下,所以并没有做出来。

首先把\(w_i,|w_i-w_j|\)的系数全都整理出来。既然每个点只能有两种选择,所以考虑最小割,设连\(S\)表示选正权值。

然后:

  1. \(a\times w_x\):连\((x,T,a\times w),(S,x,-a\times w)\)
  2. \(a|w_x-w_y|\):连\((x,y,2w),(y,x,2w)\)
  3. \(w_x=w_y\):连\((x,y,\infty),(y,x,\infty)\)
  4. \(w_x:连\((x,T,\infty),(S,y,\infty)\)
  5. \(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\)

  1. 如果\(x,那么自然谁都取不了,他对游戏胜负没有影响。
  2. 如果\(s\le x,那么\(s\)可以去别的地方抢\(b\)的,最后再回来拿掉这个,所以这堆石子一旦出现就\(s\)必胜。
  3. 如果\(b\le x<2s\),那么谁都可以取他且都只能取一次,所以这只是先后手的问题。
  4. 如果\(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;
}

转载于:https://www.cnblogs.com/p-b-p-b/p/11219229.html

你可能感兴趣的:(2019.7.20模拟赛)