Codeforces Round #369 (Div. 2)

A:略

B:

题意:给出n * n的矩阵,求一个数x,使得将矩阵中的所有0替换成x后,能使得矩阵每一行的数的和、每一列的数的和、分别两个对角线的数的和,值都相等。

题解:解方程。对于每行每列每条对角线,我求出总和 及 0个数,取两个和不相同的行或列或对角线,解方程。(原谅我的渣代码)

#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <iostream>
#include <cstring>
#include <algorithm>

#define LL long long
#define loop(a,b,c) for(int a=b;a<=c;a++)
#define rloop(a,b,c) for(int a=b;a>=c;a--)
#define clr(a,b) memset(a,b,sizeof a)
#define sd(x) scanf("%d",&x)
#define sf(x) scanf("%lf",&x)
#define ss(x) scanf("%s",x)
#define x first
#define y second
#define inf 0x3f3f3f3f
#define maxn 200005
using namespace std;
LL arr[505][505];
LL sum_row[505];
LL sum_col[505];
LL Left,Right,l,r;
int row[505];
int col[505];
int n;
bool judge(LL ans,int t1,int t2)
{

			int flag = 1;

       		LL k = sum_row[t1] + 1LL*row[t1]*ans;
            //cout<<k<<endl;
			loop(i,1,n)
			{
				if(sum_row[i] + 1LL*row[i]*ans !=k || sum_col[i] + 1LL*col[i]*ans!=k)
					flag = 0;
			}
			if(Left + 1LL*l*ans!=k || Right + 1LL*r*ans!=k)
				flag = 0;
			return flag;
}
void solve()
{
	cin>>n;
	loop(i,1,n) loop(j,1,n) cin>>arr[i][j];
	loop(i,1,n)
	{
		loop(j,1,n)
		{
			sum_col[j] += arr[i][j];
			sum_row[i] += arr[i][j];

			if(i==j) Left += arr[i][j];
			if(i==n-j+1) Right += arr[i][j];

			if(arr[i][j]==0)
			{
				row[i] ++,col[j]++;
				if(i==j) l++;
				if(i==n-j+1) r++;
			}
		}
	}
	int t1 =1,t2 = -1;
	int flag = 0;
	LL k = sum_row[1];
	loop(i,2,n)
	{
		if(sum_row[i] != k)
		{
			flag  = 1;
			t2 = i;
			break;
		}
	}
	if(t2==-1)
	{
		loop(i,1,n)
		{
			if(sum_col[i] != k)
			{
				flag  = 2;
				t2 = i;
				break;
			}
		}
		if(t2==-1)
		{
			if(Left != k)
			{
				flag  = 3;
				t2 = 0;
			}
			else if(Right != k )
			{
				flag  = 4;
				t2 = 0;
			}
		}
	}
	//cout<<flag<<" "<<t1<<" "<<t2<<endl;
	if(t2!=-1)
	{
		LL b,a;
		if(flag==1)
			a = 1LL*row[t1] - 1LL*row[t2],b = 1LL*sum_row[t2] - 1LL*sum_row[t1];
		if(flag==2)
			a = 1LL*row[t1] - 1LL*col[t2],b = 1LL*sum_col[t2] - 1LL*sum_row[t1];
		if(flag==3)
			a = 1LL*row[t1] - 1LL*l,b = 1LL*Left - 1LL*sum_row[t1];
		if(flag==4)
			a = 1LL*row[t1] - 1LL*r,b = 1LL*Right - 1LL*sum_row[t1];
        //cout<<a<<" "<<b<<endl;

        //cout<<row[t1] - row[t2]<<" "<<row[t1]<<" "<<row[t2]<<endl;

		if(   a==0 || b/a < 0||b%a!=0)
			cout<<-1<<endl;
		else
		{

			LL ans = b/a;
			if(!judge(ans,t1,t2))
				cout<<"-1"<<endl;
			else
				cout<<ans<<endl;
		}
	}
	else
	{
			if(!judge(1,1,2))
				cout<<"-1"<<endl;
			else
				cout<<1<<endl;
	}

}
int main()
{
    //freopen("data.txt","r",stdin);
    solve();
    return 0;
}
D:

题意:有一个图,有n个顶点,给出每个顶点的对应边,即 顶点i单向连接顶点a[i],我们可以任意改变其中所有边的方向(至多改变一次),为了使得图无环,那么改变的方案有多少?

题解:对于每一个环,如果有 ki 个顶点,改变方案就是 res = (2 ^ ki - 2 ) * 2 ^ ( n - sum(ki) ) (i=1,2,3....),为什么?因为对于所有环,都不会存在两两相交的情况,两两相交的意思是两个环公共一个或一个以上的顶点,那么对于环中的顶点,不妨让所有边的方向都是“逆时针”,那么逆时针的取法就是 =  C(ki,1) + C(ki,2) + ... C(ki,n-1) = 2 ^ ki - 2 ,剩下的不构成环的元素 有 n - sum(ki)个,对这些元素就有 2 ^ (n -  sum( ki ) )个方案;

。。。我用了dij + 并查集  + 快速幂 去做这道题,虽然过了,但是跑得奇慢。。。。

#include <cstdio>
#include <cmath>
#include <queue>
#include <iostream>
#include <cstring>
#include <algorithm>

#define LL long long
#define loop(a,b,c) for(int a=b;a<=c;a++)
#define rloop(a,b,c) for(int a=b;a>=c;a--)
#define clr(a,b) memset(a,b,sizeof a)
#define sd(x) scanf("%d",&x)
#define sf(x) scanf("%lf",&x)
#define ss(x) scanf("%s",x)
#define x first
#define y second
#define inf 0x3f3f3f3f
#define maxn 200005
using namespace std;
const LL mod = 1e9+7;
typedef pair<int,int> pii;
int f[maxn+100];
int to[maxn+100];
int d[maxn+100];
struct edge
{
	int to,cost;
	edge(){}
	edge(int _to,int _cost)
	{
		to = _to;
		cost = _cost;
	}
};
priority_queue<pii,vector<pii>,greater<pii> > Q;
vector<edge> G[maxn];
void addedge(int u,int v,int c)
{
	G[u].push_back(edge(v,1));
	G[v].push_back(edge(u,1));
}
void dij(int s)
{
	fill(d,d+maxn,inf);
	d[s] = 0;
	Q.push(pii(0,s));
	while(!Q.empty())
	{
		pii p = Q.top();
		Q.pop();
		int v = p.y;
		//if(d[v] < p.first) continue;
		for(vector<edge>::iterator it = G[v].begin();it!=G[v].end();it++)
		{
			edge e = *it;
			if(d[e.to] > d[v] + e.cost){
				Q.push(pii(d[e.to],e.to));
				d[e.to]  = d[v] + e.cost;
			}

		}
	}
}
void init()
{
	loop(i,1,maxn) f[i] = i;
}
int getf(int x)
{
	return f[x]==x?f[x]:getf(f[x]);
}
void unionset(int u,int v)
{
	int a = getf(u);
	int b = getf(v);
	if(a!=b)
		f[b] = a;
}
bool same(int u,int v)
{
	return getf(u)==getf(v);
}

LL quick_mod(LL x,LL n)
{
	LL res = 1;
	while(n)
	{
		if(n&1) res = 1LL*res*x%mod;
		x = 1LL*x*x%mod;
		n>>=1;
	}
	return res;
}
void solve()
{
	init();
	int n,k,tmp = 0;
	sd(n);
	k = n;
	LL ans = 1;
	loop(i,1,n)
	{
		scanf("%d",&to[i]);
		if(same(i,to[i]))
		{
			dij(i);
			tmp = d[to[i]] + 1;
			k -= tmp;
			ans = 1LL* ans * (quick_mod(2,tmp) - 2) % mod;
		}
		unionset(i,to[i]);
		addedge(i,to[i],1);
	}
	ans = 1LL * ans * quick_mod(2,k) %mod;
	printf("%I64d\n",ans);
}
int main()
{
	solve();
}

献上别人的代码,思路是一样的,但是我太菜了,没想到这样去处理。。。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <cassert>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
// head

const int N=201000;
int n,a[N],te,vis[N];
ll ret=1;
int main() {
	scanf("%d",&n);
	rep(i,1,n+1) scanf("%d",a+i); te=n;
	rep(i,1,n+1) if (vis[i]==0) {
		int x=i;
		while (1) {
			vis[x]=i;
			x=a[x];
			if (vis[x]) break;
		}
		if (vis[x]!=i) continue;
		int cyc=0,y=x;
		while (1) {
			y=a[y]; ++cyc; --te;
			if (y==x) break;
		}
		ret=ret*(powmod(2,cyc)-2)%mod;
	}
	ret=ret*powmod(2,te)%mod;
	printf("%lld\n",ret);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值