B.Black and white
题目思路
题目给出条件,(i,j),(i+1,j),(i,j+1),(i+1,j+1)中三个点被覆盖后第四个点也一定的覆盖是不需要花费的。
我们将横坐标和纵坐标看作端点来建边。
假设(i,j),(i+1,j),(i,j+1)被覆盖,那么有
A
i
<
−
>
B
j
A_i<->B_j
Ai<−>Bj,
A
i
+
1
<
−
>
B
j
A_{i+1}<->B_j
Ai+1<−>Bj,
A
i
<
−
>
B
j
+
1
A_i<->B_{j+1}
Ai<−>Bj+1这三条双向边。
有着三条边存在我们可以推出来
A
i
+
1
A_{i+1}
Ai+1 、
B
j
+
1
B_{j+1}
Bj+1之间是存在路径的。根据题目的意思他们两个点之间联通是不需要花费的。
因为题目要求的是覆盖棋盘的最小花费,我们可以将这个问题转化成将图中各点相互连通的最小花费,我们用最小生成树算法解决。
这题卡了空间,所以数组要注意,经量少用longlong
ac代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
#define pair4 pair<pair<int,int>,pair<int,int> >
#define fi first
#define se second
using namespace std;
const int maxn = 1e4+10;
const int inf = 1e9+10;
const ll llinf =1e18+10;
const ll mod = 1e9+7;
int n,m,a,b,c,d,p;
int e[maxn][maxn],dis[maxn*2];
bool book[maxn*2];
void init()
{
for(int i=1; i<=n+m; i++)
for(int j=1; j<=n+m; j++)
if(i==j)e[i][j]=0;
else
e[i][j]=inf;
}
ll prim(int n)
{
ll ans=0;
memset(book,false,sizeof(book));
book[1]=true;
for(int i=1; i<=n; i++)
dis[i]=e[1][i];
for(int i=2; i<=n; i++)
{
ll minn=llinf,u=-1;
for(int j=1; j<=n; j++)
{
if(!book[j]&&dis[j]<minn)
{
minn=dis[j];
u=j;
}
}
if(ans==inf)
return -1;
ans+=minn;
book[u]=true;
for(int v=1; v<=n; v++)
if(!book[v])
dis[v]=min(dis[v],e[u][v]);
}
return ans;
}
int main()
{
scanf("%d%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d,&p);
init();
ll tem=a;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int id=(i-1)*m+j;
tem=(tem%p*tem%p*b%p+tem*c%p+d%p)%p;
e[i][j+n]=e[j+n][i]=tem;
}
}
n=n+m;
cout<<prim(n)<<endl;
}
这题结论其实挺好想的,但是我估计我直接看这道题的话很难想到建边啥的。。。。
J.Counting Triangles
题目思路
因为我们要找的是三个点,数据也不支持
n
3
n^3
n3枚举,所以反向思考找异色三元组数量,最后从总数中减去,剩下的就是同色了。
因为只用两种颜色涂色,所以异色三元组会有两条边相同,剩下那条边异色。
根据这个性质,我们可以直接枚举起点,再枚举每个起点的边,记录颜色为1的和为0的,每次更新前计算他的贡献就好。
ac代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
#define pair4 pair<pair<int,int>,pair<int,int> >
#define fi first
#define se second
using namespace std;
const int maxn = 3e5+10;
const int inf = 1e9+10;
const ll llinf =1e18+10;
const ll mod = 1e9+7;
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;
bool edge[8005][8005];
int n, seed;
vector<int>vec1[8005],vec0[8005];
int main() {
cin >> n >> seed;
srand(seed);
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
{
edge[j][i] = edge[i][j] = read();
}
}
ll ans=1LL*n*(n-1)*(n-2)/6;
ll res=0;
for (int i=1; i<=n; i++){
int black=0,white=0;
for (int j=1; j<=n; j++){
if (i==j) continue;
if (edge[i][j])
{
res+=white;
black++;
}
else
{
res+=black;
white++;
}
}
}
ans-=res/2;
printf("%lld\n",ans);
return 0;
}
F. 24dian
题目思路
搜索模拟题。我们先找到所有元素为n的集合,然后去暴力判断它是否满足情况。
我一开始的想法是枚举四个位置上的数,然后往中间差符号。
写到后面发现答案一直多一些结果,发现我这样枚举少了一些情况,例如:(a+b) * (c+d)、(a * b) + (c * d)等。
最后把这些情况枚举一了一遍就ac了。
ac代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
#define pair4 pair<pair<int,int>,pair<int,int> >
#define fi first
#define se second
using namespace std;
const int maxn = 2e5+10;
const int inf = 1e9+10;
const ll llinf = 1e18+10;
const ll mod = 1e9+7;
int n;
double m;
vector<double>vec,an[13*13*13*13+100];
double a[maxn];
int cnt=0,flagx=0,flagy=0;
queue<double>q;
bool judge(double x,double y) {
if (x>(int)x+1e-9) return 1;
if (y>(int)y+1e-9) return 1;
if (x/y>(int)(x/y)+1e-9) return 1;
return 0;
}
void dfs1(int x,double ans,int flag)
{
if(x==n)
{
//printf("%lf ",ans);
if(ans<0)ans*=-1;
if(fabs(ans-m)<=1e-9)
{
flagx++;
if(flag)
flagy++;
}
return;
}
dfs1(x+1,a[x]+ans,flag);
dfs1(x+1,ans-a[x],flag);
dfs1(x+1,ans*a[x],flag);
int temflag=0;
if(judge(ans,a[x]))
{
temflag=1;
}
dfs1(x+1,ans/a[x],temflag|flag);
if(ans==0.0)return;
temflag=0;
if(judge(a[x],ans))
{
temflag=1;
}
dfs1(x+1,a[x]/ans,temflag|flag);
}
void check(double x,double y,int flag)
{
if(y==-1.0)
{
if(x<0)x*=-1;
if(fabs(x-m)<=1e-9)
{
flagx++;
if(flag)
flagy++;
}
return;
}
check(x+y,-1.0,flag);
check(x-y,-1.0,flag);
check(x*y,-1.0,flag);
if(y!=0.0)
check(x/y,-1,flag|judge(x,y));
if(x!=0.0)
check(y/x,-1,flag|judge(y,x));
}
bool ck()
{
for(int i=0;i<vec.size();i++)
a[i]=vec[i];
flagx=0;
flagy=0;
do{
dfs1(1,a[0],0);
double x=a[0],y=a[1],c=a[2],d=a[3];
check(x+y,c+d,0);
check(x+y,c-d,0);
check(x+y,c*d,0);
check(x+y,c/d,judge(c,d));
check(x+y,d/c,judge(d,c));
check(x-y,c+d,0);
check(x-y,c-d,0);
check(x-y,c*d,0);
check(x-y,c/d,judge(c,d));
check(x-y,d/c,judge(d,c));
check(x*y,c+d,0);
check(x*y,c-d,0);
check(x*y,c*d,0);
check(x*y,c/d,judge(c,d));
check(x*y,d/c,judge(d,c));
check(x/y,c+d,judge(x,y));
check(x/y,c-d,judge(x,y));
check(x/y,c*d,judge(x,y));
check(x/y,c/d,judge(x,y)|judge(c,d));
check(x/y,d/c,judge(x,y)|judge(d,c));
check(y/x,c+d,judge(y,x));
check(y/x,c-d,judge(y,x));
check(y/x,c*d,judge(y,x));
check(y/x,c/d,judge(y,x)|judge(c,d));
check(y/x,d/c,judge(y,x)|judge(d,c));
}while(next_permutation(a,a+n));
if(flagx>0&&flagy>0&&flagx==flagy)
{
return true;
}else
{
return false;
}
}
void dfs(int x,int pre)
{
if(x>=n+1)
{
if(ck())
{
an[++cnt]=vec;
}
return;
}
for(double i=pre;i<=13;i++)
{
vec.push_back(i);
dfs(x+1,i);
vec.pop_back();
}
}
int main()
{
scanf("%d%lf",&n,&m);
dfs(1,1);
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
{
for(int j=0;j<an[i].size();j++)
{
printf("%.0lf ",an[i][j]);
}
printf("\n");
}
}
C.Minimum grid
题目思路
根据题目意思,我们发现当行和列的最大值k相同时,我们就能少防止一个k值。
所以我们要求最小花费,就一定要最大化这种情况。
如果我们将行和列看作一个二分图两侧的点,那么上面说的这种情况很想二分图左右两个点匹配上。
所以我们考虑建二分图求解。
我们记录每个最大值k所对应的行和列。对于同一个k,我们将对应的行列建二分图,跑最大匹配,最后这个k的总贡献就是(行的个数+列的个数-最大匹配个数)*k。
我们枚举所有的k,最后的总值就是答案了
我用的是最大流求最大匹配
ac代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pl printf("\n");
#define pb(x) push_back(x)
#define pair4 pair<pair<int,int>,pair<int,int> >
#define fi first
#define se second
using namespace std;
const int maxn = 2e5+10;
const int inf = 1e9+10;
const ll llinf = 1e18+10;
const ll mod = 1e9+7;
int n,m,k;
int mp[2000+10][2000+10];
int s,t,cnt;
int first[maxn];
int dep[maxn];
int cur[maxn];
struct node
{
int to,next,w;
}e[maxn];
inline bool bfs()
{
for(int i=s;i<=t;i++)dep[i]=-1;
dep[s]=0;
for(int i=s;i<=t;i++)cur[i]=first[i];
queue<int>q;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=first[u];i;i=e[i].next)
{
int to=e[i].to;
ll vol=e[i].w;
if(vol>0&&dep[to]==-1)
{
dep[to]=dep[u]+1;
q.push(to);
}
}
}
return dep[t]!=-1;
}
inline int dfs(int u=s,int flow=inf)
{
if(u==t)
return flow;
ll last=flow;
for(int i=cur[u];i&&last;i=e[i].next)
{
cur[u]=i;
int to=e[i].to;
ll vol=e[i].w;
if(vol>0&&dep[to]==dep[u]+1)
{
int c=dfs(to,min(vol,last));
last-=c;
e[i].w-=c;
e[i^1].w+=c;
}
}
return flow-last;
}
inline int dinic()
{
int ans=0;
while(bfs())
{
ans+=dfs();
}
return ans;
}
vector<int>vec[maxn],r,c;
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].next=first[u];
first[u]=cnt;
}
int solve()
{
for(int i=s;i<=t;i++)
first[i]=0,cur[i]=0;
cnt=1;
for(int i=0;i<r.size();i++)
{
add(s,r[i],1);
add(r[i],s,0);
}
for(int i=0;i<c.size();i++)
{
add(c[i]+n,t,1);
add(t,c[i]+n,0);
}
for(int i=0;i<r.size();i++)
{
for(int j=0;j<c.size();j++)
{
int u=r[i];
int v=c[j];
if(mp[u][v])
{
add(u,v+n,1);
add(v+n,u,0);
}
}
}
return dinic();
}
int main()
{
int x;
scanf("%d%d%d",&n,&m,&k);
s=0,t=2*n+1;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
vec[x].push_back(i);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
vec[x].push_back(i+n);
}
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
mp[u][v]=1;//这个地方之前加上了mp[v][u]=1,一直wa。。
}
ll ans=0;
for(int i=k;i>0;i--)
{
for(int j=0;j<vec[i].size();j++)
{
int id=vec[i][j];
if(id<=n)
{
r.push_back(id);
}else
{
c.push_back(id-n);
}
}
ans+=(r.size()+c.size()-solve())*1LL*i;
r.clear();
c.clear();
}
printf("%lld\n",ans);
}