题赛地址
C题:(三元环计数)
题意,给定一个图,求四元环的个数,这个四元环要求是由两个三元环相邻一条共同边组成的
解法:
①统计每个点的度数
②入度<=sqrt(m)的分为第一类,入度>sqrt(m)的分为第二类
③对于第一类,暴力每个点,然后暴力这个点的任意两条边,再判断这两条边的另一个端点是否连接
因为m条边最多每条边遍历一次,然后暴力的点的入度<=sqrt(m),所以复杂度约为O(msqrt(m))
④对于第二类,直接暴力任意三个点,判断这三个点是否构成环,因为这一类点的个数不会超过sqrt(m)个,所以复杂度约为O(sqrt(m)3)=O(msqrt(m))
⑤判断两个点是否连接可以用set,map和Hash都行,根据具体情况而行
这种做法建的是双向边,常数很大
//https://www.cnblogs.com/jiachinzhao/p/7474761.html
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
sets;
int deg[maxn];
vectorg[maxn];
int vis[maxn], vi[maxn];
int main() {
int n, m, u, v, sz;
while (scanf("%d%d", &n, &m) != EOF) {
sz = sqrt(m + 0.5);
s.clear();
for (int i = 1; i <= n; i++) {
vis[i] = vi[i] = deg[i] = 0;
g[i].clear();
}
for (int i = 0; i < m; i++) {
scanf("%d%d", &u, &v);
s.insert(u + 1ll * v*n);
s.insert(v + 1ll * u*n);
deg[u]++, deg[v]++;
g[u].push_back(v); g[v].push_back(u);
}
ll ans = 0;
for (int u = 1; u <= n; u++) {
vis[u] = 1;
for (auto v : g[u])vi[v] = u;
for (auto v : g[u]) {
int cnt = 0;
if (vis[v])continue;
if (deg[v] <= sz) {
for (auto vv : g[v])
if (vi[vv] == u)cnt++;
}
else {
for (auto vv : g[u]) {
if (s.find(1ll * v*n + vv) != s.end())cnt++;
}
}
ans += 1ll * cnt*(cnt - 1) / 2;
}
}
printf("%lld\n", ans);
}
return 0;
}
D(矩阵快速幂)
题意:有一个4*n的矩阵,要求只能只能用2x1的木板覆盖,问一共有多少种覆盖的方法
解法:
当前一列已经铺满的时候,那么下一列一共有五种铺法,根据这五种铺发推出递推关系式,之后矩阵快速幂跑一下即可
博客链接
//https://blog.csdn.net/elbadaernu/article/details/77825979
#include
#include
#include
using namespace std;
#define LL long long
const int mod = 1000000007;
struct matrix
{
LL x[4][4];
};
matrix mutimatrix(matrix a, matrix b)
{
matrix temp;
memset(temp.x, 0, sizeof(temp.x));
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
for (int k = 0; k < 4; k++)
{
temp.x[i][j] += a.x[i][k] * b.x[k][j];
temp.x[i][j] %= mod;
}
return temp;
}
matrix k_powmatrix(matrix a, LL n)//矩阵快速幂
{
matrix temp;
memset(temp.x, 0, sizeof(temp.x));
for (int i = 0; i < 4; i++)
temp.x[i][i] = 1;
while (n)
{
if (n & 1)
temp = mutimatrix(temp, a);
a = mutimatrix(a, a);
n >>= 1;
}
return temp;
}
int main(){
LL n;
while (scanf("%lld", &n) != EOF){
//前面四个手算下
if (n == 1){printf("1\n");continue;}
if (n == 2){printf("5\n");continue;}
if (n == 3){printf("11\n");continue;}
if (n == 4){printf("36\n");continue;}
matrix st;
memset(st.x, 0, sizeof(st.x));
st.x[0][0] = 1;
st.x[1][0] = 5;
st.x[2][0] = 1;
st.x[3][0] = -1;
st.x[0][1] = 1;
st.x[1][2] = 1;
st.x[2][3] = 1;
matrix init;//初始矩阵
memset(init.x, 0, sizeof(init.x));
init.x[0][0] = 36;
init.x[0][1] = 11;
init.x[0][2] = 5;
init.x[0][3] = 1;
st = k_powmatrix(st, n - 4);//经过n-4次相乘
st = mutimatrix(init, st);//然后再乘上初始矩阵
printf("%lld\n", (st.x[0][0] + mod) % mod);
}
return 0;
}
E:
题意:
有一列数,每次询问随机的删掉一个数,求最后所有数且、或、异或的结果
解法:
按位处理,每一位统计一的个数,之后按一的个数为0,为1,为n,以及其他情况分类讨论即可
#include
#include
using namespace std;
const int maxn = 1e6 + 5;
int n, m;
int a[maxn];
int b[maxn];
int main() {
while (~scanf("%d %d", &n, &m)) {
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
int AND = 0, OR = 0, XOR = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
int t = a[i], k = 0;
XOR ^= a[i];
while (t) { b[k++] += t % 2; t /= 2; }
}
while (m--){
int x; scanf("%d", &x);
x = a[x];
int NXOR = XOR^x;
AND = 0, OR = 0;
for (int j = 0; j < 32; j++) {
if (b[j] == 0)continue;
else if (b[j] == 1) {
if (x&(1 << j))continue;
OR += (1 << j);
if (n == 2)AND += (1 << j);
}
else if (b[j] == n) {
AND += (1 << j); OR += (1 << j);
}
else {
OR += (1 << j);
if (b[j] == n - 1 && !(x&(1 << j)))
AND += (1 << j);
}
}
printf("%d %d %d\n", AND, OR, NXOR);
}
}
return 0;
}
H:(打表+数论)
题意:
给定一个数n和a,问在 [1,1 << n] 的范围内有多少个b满足 a^b=b^a(mod 1 << n)
解法:
关键是思路!
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXM=100010;
const long long MOD=1000000007;
const double PI=acos(-1);
long long n,a;
long long qpow(long long x,long long y,long long mod)
{
long long res=1;
while(y)
{
if (y&1) res=(res*x)%mod;
x=(x*x)%mod;
y=y>>1;
}
return res;
}
long long qpow(long long x,long long y)
{
long long res=1;
while(y)
{
if (y&1) res=res*x;
x=x*x;
y=y>>1;
}
return res;
}
int main()
{
while(scanf("%lld%lld",&n,&a)!=EOF)
{
if (a&1)
{
printf("1\n");
continue;
}
else
{
long long m=1<