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);
}