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;
}
posted @ 2019-07-20 20:59  p_b_p_b  阅读(255)  评论(0编辑  收藏  举报