B Black and white
这题直接将行列转换成点,而把点看成两个行列点的边,这样就转换成了当行列点在一个连通块的时候,再来个已经是连通块的点的时候,就不需要加上他的权值了,所以一共有n + m 个点,最后又要使得所有点是连通的,也就是如果再加一条边一定可以形成环,而这条环是我们不需要加的,所以直接就转换成了最小生成树,n + m - 1条边,prim直接求。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5005, INF = 0X3F3F3F3F;
int f[N * N];
int n,m;
int g[N * 2][N * 2];
int st[N * 2];
int dist[N * 2];
int k[N * 2];
int prim(){
memset(dist, INF, sizeof dist);
int res = 0;
dist[1] = 0;
for(int i = 0; i < n + m; i++){
int t = -1;
for(int j = 1; j <= n + m; j++) if(!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
st[t] = true;
if(dist[t] != INF) res += dist[t];
for(int j = 1; j <= n + m; j++) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
int main()
{
int a,b,c,d,p;
scanf("%d %d %d %d %d %d %d",&n,&m,&a,&b,&c,&d,&p);
f[0] = a;
for(int i = 1;i <= n * m;i ++ ) f[i] = (1ll * f[i - 1] * f[i - 1] * b + f[i - 1] * c + d) % p;
memset(g,INF,sizeof g);
for(int i = 1; i <= n + m; i++) g[i][i] = 0;
int cnt = 0;
for(int i = 1;i <= n;i ++ )
{
for(int j = n + 1;j <= n + m;j ++ )
{
g[i][j] = f[m * (i - 1) + j - n];
g[j][i] = f[m * (i - 1) + j - n];
}
}
printf("%d\n",prim());
}
C Minimum grid
这题和上面一道题一样,也是将行列转换成点,由于我们只能在特定位置填数,所以只能在对应的行列加上边,然后从大到小枚举最大值,我们可以发现,行点是不会有边的,列点之间也不会有边,这不就是典型的二分图吗》》》。所以我们每次求一下当前二分图的最大匹配,假如我们对于当前的行列都填上这个最大值,显然不是最优的,但是如果当前行和当前列有相交的点可以填数,我当然把数填到这里,这样只用了一个数就满足了当前行列的最大值,这就是一个匹配数,所以我们求出最大匹配数,再用行列之和减去最大匹配数,就是当前最最大值的贡献了。
#include<bits/stdc++.h>
using namespace std;
const int N = 2010, M = 4010, A = 1e6 + 10;
int a[N],b[N];
int h[M],e[M],ne[M],idx;
int g[N][N];
vector<int> row[A],col[A];
bool st[M];
int match[M];
int n,m,k;
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx ++;
}
bool dfs(int u)
{
for(int i = h[u];i != -1;i = ne[i])
{
int j = e[i];
if(!st[j])
{
st[j] = true;
if(match[j] == 0 || (dfs(match[j])))
{
match[j] = u;
return true;
}
}
}
return false;
}
int solve()
{
memset(match,0,sizeof match);
int res = 0;
for(int i = 1;i <= n;i ++ )
{
memset(st,false,sizeof st);
if(dfs(i)) res ++;
}
return res;
}
int main()
{
scanf("%d %d %d",&n,&m,&k);
for(int i = 1;i <= n;i ++ )
{
scanf("%d",&a[i]);
row[a[i]].push_back(i);
}
for(int i = 1;i <= n;i ++ )
{
scanf("%d",&b[i]);
col[b[i]].push_back(i + n);
}
while(m -- )
{
int a,b;
cin >> a >> b;
g[a][b] = 1;
}
int ans = 0;
for(int x = k;x >= 0;x -- )
{
if(row[x].size() == 0 && col[x].size() == 0) continue;
//建图
memset(h,-1,sizeof h);
idx = 0;
for(auto i : row[x])
{
for(auto j : col[x])
{
if(g[i][j - n] != 0) add(i,j), add(j,i);
}
}
int res = solve();
//cout << res << endl;
ans += (col[x].size() + row[x].size() - res) * x;
}
printf("%d\n",ans);
return 0;
}
E Math
这题公式推导就不写了,虽然赛前不会,但是赛后感觉就挺简单的。后来队友又做了一道题,他说好像也用到了这种方法,感觉这个方法应对这种题还是不错,不过我觉得打表也很好,直接找规律,毕竟赛时紧张,可能不会去推。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6;
typedef double ll;
const ll INF = 1e18;
ll a[7000007];
int tot = 0;
void init(){
for(int i=2;i<=N;i++){
ll x=1ll*i*i*i;
a[++tot]=x;
ll t=x*i*i-i;
if(t>INF)continue;
ll prea=x;
while(t<INF){
a[++tot]=t;
t=1ll*t*i*i-prea;
prea=a[tot];
}
}
sort(a+1,a+1+tot);
}
int main()
{
init();
int t;
cin >> t;
while(t -- )
{
ll n;
scanf("%lf",&n);
int pos = lower_bound(a + 1,a + 1 + tot,n) - a;//
if(a[pos] > n) pos --;
printf("%d\n",pos + 1);
}
}
F 24dian
这题暴力枚举,深搜。不过我dfs还不太会,感觉有些时候想不通,应该是人脑的栈不够用吧。。。。直接把所有情况列举出来,判断当前经过±*/后是否可以等于给定值,但是注意运算过程中要出现分数,我之前还以为到最终结果之前每一步都要出现分数,结果看了下代码,发现对于这种方式,只需要出现过就行了。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6;
const double eps = 1e-5;
int n,m;
struct node
{
int a[4];
};
vector<node> ans;
double a[4];
bool frac, is;
bool vis[N];
void dfs(int cnt,bool flag)
{
if(cnt == n)
{
if(fabs(a[0] - m) < eps)
{
is = true;
if(!flag) frac = true;
return ;
}
}
// 判断是否出现了分数只要一出现,frac = true
for(int i = 0;i < n;i ++ ) if(fabs(floor(a[i]) - a[i]) > eps) flag = true;
for(int i = 0;i < n;i ++ )
{
for(int j = i + 1;j < n;j ++ )
{
if(vis[i] || vis[j]) continue;
vis[j] = true;
double x = a[i], y = a[j];
a[i] = x + y;
dfs(cnt + 1,flag);
a[i] = x - y;
dfs(cnt + 1,flag);
a[i] = y - x;
dfs(cnt + 1,flag);
a[i] = x * y;
dfs(cnt + 1,flag);
if(fabs(y) > eps)
{
a[i] = x / y;
dfs(cnt + 1,flag);
}
if(fabs(x) > eps)
{
a[i] = y / x;
dfs(cnt + 1,flag);
}
vis[j] = false;
a[i] = x, a[j] = y;
}
}
}
void DFS(int cnt,int x)
{
if(cnt == n)
{
frac = false, is = false;
dfs(1,false);
if(!frac && is)
{
ans.push_back((node){(int)a[0],(int)a[1],(int)a[2],(int)a[3]});
}
return ;
}
for(int i = x;i <= 13;i ++ )
{
a[cnt] = i;
DFS(cnt + 1,i);
}
}
int main()
{
scanf("%d %d",&n,&m);
DFS(0,1);
cout << ans.size() << endl;
for(int i = 0;i < ans.size();i ++ )
{
for(int j = 0;j < n;j ++ )
cout << ans[i].a[j] << " \n"[j == n - 1];
}
return 0;
}```
J Counting Triangles
这题虽然是签到,但是我觉得真的是好题呀,一看一大片人不会,赛后一看题解,立马会了。。。题解已经很清楚了。再附上一道acwing额的题https://www.acwing.com/problem/content/3781/
#include<bits/stdc++.h>
namespace GenHelper
{
unsigned z1,z2,z3,z4,b,u;
unsigned get()
{
b=((z1<<6)^z1)>>13;
z1=((z1&4294967294U)<<18)^b;
b=((z2<<2)^z2)>>27;
z2=((z2&4294967288U)<<2)^b;
b=((z3<<13)^z3)>>21;
z3=((z3&4294967280U)<<7)^b;
b=((z4<<3)^z4)>>12;
z4=((z4&4294967168U)<<13)^b;
return (z1^z2^z3^z4);
}
bool read() {
while (!u) u = get();
bool res = u & 1;
u >>= 1; return res;
}
void srand(int x)
{
z1=x;
z2=(~x)^0x233333333U;
z3=x^0x1234598766U;
z4=(~x)+51;
u = 0;
}
}
using namespace GenHelper;
using namespace std;
typedef long long ll;
const int N = 8010;
bool edge[8005][8005];
ll white[N], black[N];
ll res = 0;
int main() {
int n, seed;
cin >> n >> seed;
srand(seed);
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
edge[j][i] = edge[i][j] = read();
for(int i = 0;i < n;i ++ )
for(int j = i + 1;j < n;j ++ )
if(edge[i][j]) black[i] ++,black[j] ++;
else white[i] ++, white[j] ++;
res = (ll)n * (n - 1) * (n - 2) / 6;
ll s = 0;
for(int i = 0;i < n;i ++ )
{
s += (ll)white[i] * black[i];
}
s /= 2;
printf("%lld\n",res - s);
return 0;
}