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<w_y\):连\((x,T,\infty),(S,y,\infty)\)。
  5. \(w_x\le w_y\):连\((x,y,\infty)\)。

注意到只有第一种边有负权,而这种边一定会恰好删去\(n\)个,所以可以每条边加上一个数,最后再减去。

#include<bits/stdc++.h>
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<typename T>
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
temp bool chkmax(T &x,T y){return x<y?x=y,1:0;}
#define ll long long
#define db double
void file()
{
    #ifdef NTFAKIOI
    freopen("a.in","r",stdin);
    #endif
}
//void chktime()
//{
//  #ifdef NTFAKIOI
//  cout<<1.0*(clock()-__t)/1000;
//  #endif
//}
temp void read(T &x)
{
    x=0;char ch=getchar(),t=0;
    while (ch>'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<int,int>
#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()
{
    queue<int>q;
    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<s\),那么自然谁都取不了,他对游戏胜负没有影响。
  2. 如果\(s\le x<b\),那么\(s\)可以去别的地方抢\(b\)的,最后再回来拿掉这个,所以这堆石子一旦出现就\(s\)必胜。
  3. 如果\(b\le x<2s\),那么谁都可以取他且都只能取一次,所以这只是先后手的问题。
  4. 如果\(2s\le x<s+b\),那么就是最麻烦的了。如果\(s\)取了他,那么就变成了第二种情况,\(s\)必胜。所以不管谁先手,都会先去取这种石头堆。如果只有一个,那么得看谁先手。否则,\(b\)无论如何都不可能取完,\(s\)总是要赢的。

说完这些,做法也就明朗了,无非就是分情况二、三、四有几个来分类讨论。

#include<bits/stdc++.h>
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<typename T>
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
temp bool chkmax(T &x,T y){return x<y?x=y,1:0;}
#define ll long long
#define db double
void file()
{
    #ifdef NTFAKIOI
    freopen("a.in","r",stdin);
    #endif
}
//void chktime()
//{
//  #ifdef NTFAKIOI
//  cout<<1.0*(clock()-__t)/1000;
//  #endif
//}
temp void read(T &x)
{
    x=0;char ch=getchar(),t=0;
    while (ch>'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<int,int>
#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) ++m1;
        else if (x<b) ++m2;
        else if (x-a>=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

时间: 2024-08-01 02:00:57

2019.7.20模拟赛的相关文章

【题解】2019,4.20模拟赛 (白鼠)

\(Description:\) 给出一块巧克力,横着可以切 \(h\) 刀,竖着可以切 \(w\) 刀,横着切了 \(i\) ,竖着切了 \(j\) 可以得到 \((i+1)*(j+1)\) 块巧克力,每次切得代价是巧克力块数,求切 \(k\) 的期望代价,每次在剩余能切的位置等概率选一个切,答案对 \(10^9+7\) 取模. \(Sample\) \(Input:\) 2 1 2 \(Sample\) \(Output:\) 666666677 $Solution: $ 一开始考试的时候没

2019.10.24模拟赛赛后总结

本文原创,如果有不到位的地方欢迎通过右下角的按钮私信我! A.Icow Player 题目描述 被无止境的农活压榨得筋疲力尽后,Farmer John打算用他在MP3播放器市场新买的iCow来听些音乐,放松一下.FJ的iCow里存了N(1 <= N <= 1,000)首曲子,按1..N依次编号.至于曲子播放的顺序,则是按一个Farmer John自己设计的算法来决定: * 第i首曲子有一个初始权值R_i(1 <= R_i <= 10,000). * 当一首曲子播放完毕,接下来播放的

『8.20 模拟赛』旋转的多边形

题目链接戳着里!! 题目描述 解题思路 显然,多边形滚动的时候,指定的点一定是绕着某一个顶点旋转的,旋转的半径就是点到顶点的距离,角度就是顶点所在脚的外角. 如下图所示: 那么我们的问题就转化成了求dis和θ了. dis很简单,只要勾股定理就好了. 那θ呢?也很简单喽,只要链接当前顶点的相邻的两个顶点,运用余弦定理求就好啦,注意一下角度值和弧度制就可以了. (不会余弦定理的小伙伴戳这里==>  Wikipedia & 百度百科) 代码 1 #include<iostream> 2

18.9.20模拟赛T2 城市 枚举

题目大意:给出$N$个数两两的和共$\frac{N \times (N-1)}{2}$个数,请你求出原来的$N$个数输入:第一行一个数$N$,第二行$\frac{N \times (N-1)}{2}$个数表示两两之和(不保证有序)输出:第一行为可行解个数$K$,接下来$K$行每行一个方案,每一个方案的数字从小到大输出,中间有一个空格:方案按字典序从大到小输出.sample input:4 3 6 5 4 5 7 sample output:11 2 3 4数据范围:$2 \leq N \leq

【单调栈维护连续区间】2019.1.18模拟赛T2 浇花

这道题是一道单调栈的题 1 题目描述 2 JDFZ在餐厅门前种了一排nn棵花,每棵花都有一个高度.浇花大爷会枚举所有的区间,然后从区间中找出一个高度最矮的花进行浇水.由于浇花大爷浇完水之后就精疲力竭了,所以请你帮助他计算每棵花都被浇了几次水. 3 4 输入格式 5 第一行一个整数nn. 第二行nn个整数,分别表示每棵花的高度. 6 7 输出格式 8 一行nn个整数用空格隔开,分别表示每棵花被浇了几次水. 9 10 样例一 11 input 12 3 13 1 3 5 14 output 15 3

2019.10.26模拟赛

T1 序列 给定一长度为\(n\)的序列\(s\),定义其健美值为:\[\sum\limits_{i=1}^{n}|s_i - i|\]因为 zzq 喜欢健美,所以 zzq 希望减小\(s\)的健美值,以衬托 zzq 的健美.为了达到 zzq 的目的,zzq 希望你对序列进行旋转操作,一次旋转操作可以使序列中的所有元素前移一位,并使\(s_1\)移动到\(s_n\). 可以进行任意次旋转操作,zzq 希望旋转后的健美值最小,请找出这个最小值. SOV 智商检测题 我们发现对于每个数,移动每一次会

『8.20 模拟赛』冒泡排序

题目描述 给定n,k,和一个长度为n的序列,请输出这个序列冒泡排序k次之后的结果. 解题思路 我们观察上面给出的伪代码,可以发现这是一段把代码排序成升序的代码,那我们来考虑一下冒牌排序的几个特征. 一个大的数要向右交换,但是一次交换之后就可以换很多位置,所以换一次就不知道跑到哪里去了,所以很难维护. 一个小的数每次最多和它左边的更大的数交换一次,所以一次最多向左边跑一个位置,比大数不知道好维护到那里去了. 进一步思考,k次交换之后,最多可以向左跑k步,也就是说k+1位置的数最远能跑到1的位置上来

2019.08.06模拟赛T2

题目大意 已知三个$n$位二进制数$A$,$B$,$C$. 满足: $A+B=C$ 它们二进制位中$1$的个数分别为$a$,$b$,$c$. 求满足条件的最小的$C$. Solution 唉,又是一道随缘猜结论的题,可惜极限数据卡掉了我一个点,开大数组就A了..... 通过打表,我们发现所有的最优解中都有一种情况是$A$的二进制位的$1$是连续一段. 原文地址:https://www.cnblogs.com/ldysy2012/p/11311281.html

2019.9.26模拟赛

T1序列 这个题大佬们爆踩std了 给一个序列,其中一段排序后是某一等比数列子序列,求最长长度. 怎么做 数据太水导致枚举公比的过了 序列中每两个数做商,如果整除了的话,这个商一定是公比的次幂.我们从大到小枚举它是公比的几次幂,从而求到最小的公比.由于\(2^{60} > 1^{18}\),这个枚举只需要从59开始即可.时间复杂度\(O(64n)\) Code pow有精损注意判一下.(不要瞎搞手写分数次幂) #include <bits/stdc++.h> namespace fdat