退役后的第一次补题
果然做题的时间拉长了好多
但是挺多题自己想出来了
也是蛮爽的
UPD:8.11 明早还要上课,能写多少写多少吧。
UPD:8.12 F留坑待填
A - Getting Difference
有点翻车,A想了好久才弄出来
题意:
有
N
个球,第
- 拿出两个球上面分别写着
x
,
y ,放回去三个球上面分别写着 x ,y , |x−y|
询问是否能在若干次操作后使得序列中存在球的值为
K
解答:
联系欧几里得算法,可以的得出进行若干次操作后,我们能得到的最小的数,是这些数的gcd。判断
K
是否小于
时空复杂度:
O(N)
#include <bits/stdc++.h>
#define N 1000500
using namespace std;
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int n,k,mx,a[N];
int main() {
n = rd(), k = rd();
for (int _=1;_<=n;_++) a[_] = rd(), mx = max(mx, a[_]);
int g = a[1];
for (int _=2;_<=n;_++) g = __gcd(g, a[_]);
if (k <= mx && (mx-k)%g == 0) puts("POSSIBLE"); else puts("IMPOSSIBLE");
return 0;
}
B - Sports Festival
题意:
有
N
个人参加运动会,有
每个人有一个
1
~
作为主办方,你现在要选取一些运动,举办这些运动的比赛。
运动员会参加被举办的运动中,自己最喜欢的那一个运动参加。
你需要选出一些运动,使得举办这些运动时,参加人数最多的那一项运动参赛人数最少,输出这个人数。
1≤M≤300
,
Ai1
,
Ai2
, … ,
AiM
是一个
1
-
解答:
不妨先选取所有的运动。
考虑目前参赛人数最多的运动
x
,当且仅当我们不选择该项运动,最后的答案会更优。
所以我们每次删去参赛人数最多的运动,然后对于每一个人分别统计答案。
时空复杂度:
#include <bits/stdc++.h>
#define N 305
using namespace std;
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int n,m,a[N][N],vis[N],cnt[N];
int main() {
n = rd(), m = rd();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) a[i][j] = rd();
for (int i=1;i<=m;i++) vis[i] = 1;
int ans = n;
for (int _=1;_<=m;_++) {
memset(cnt,0,sizeof(cnt));
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (vis[ a[i][j] ]) {
cnt[ a[i][j] ]++;
break;
}
int mx = 0, id = 0;
for (int i=1;i<=m;i++)
if (cnt[i] >= mx)
mx = cnt[i], id = i;
ans = min(ans, mx);
vis[id] = 0;
}
printf("%d\n",ans);
return 0;
}
C - Coins
题意:
现在有
X+Y+Z
个人,第
i
个人有
每个人只能给你三种硬币中的一种,你需要指定
X
个人给金币,
求能得到硬币的最大值。
1≤Y
1≤Z
X+Y+Z≤105
1≤Ai≤109
1≤Bi≤109
1≤Ci≤109
解答:
令
n=X+Y+Z
考虑
Z
等于零的情况
此时我们可以按照
当
Z
不为零的时候,相当于在排序后的序列上拿出
那么序列中显然存在一个或多个位置,使得在这个位置之前的所有元素选择
Ai
或
Ci
,在这个位置之后的所有元素选择
Bi
或
Ci
爆枚这个位置,用堆维护元素的差值,算出在
[1,x]
区间以及
(x,n]
区间中最优的和,更新答案即可。
时间复杂度:
O(Nlog2N)
,空间复杂度:
O(N)
#include <bits/stdc++.h>
#define N 200050
using namespace std;
typedef long long LL;
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
struct HbFS{int a,b,c;}Q[N];
bool cmp(HbFS p1, HbFS p2) {return p1.a-p1.b > p2.a-p2.b;}
priority_queue<int, vector<int>, greater<int> > q1,q2;
int x,y,z,n;
LL L[N],R[N];
int main() {
x = rd(), y = rd(), z = rd();
n = x + y + z;
for (int _=1;_<=n;_++)
Q[_].a = rd(), Q[_].b = rd(), Q[_].c = rd();
sort(Q+1,Q+n+1,cmp);
for (int _=1;_<=x;_++)
q1.push(Q[_].a - Q[_].c), L[_] = L[_-1] + Q[_].a;
for (int _=x+1;_<=n;_++) {
int cur = Q[_].a - Q[_].c;
L[_] = L[_-1];
if (cur > q1.top()) {
int tmp = q1.top();
q1.pop();
q1.push(cur);
L[_] -= tmp;
L[_] += Q[_].a;
} else
L[_] += Q[_].c;
}
for (int _=n;_>=n-y+1;_--)
q2.push(Q[_].b - Q[_].c), R[_] = R[_+1] + Q[_].b;
for (int _=n-y;_>=1;_--) {
int cur = Q[_].b - Q[_].c;
R[_] = R[_+1];
if (cur > q2.top()) {
int tmp = q2.top();
q2.pop();
q2.push(cur);
R[_] -= tmp;
R[_] += Q[_].b;
} else
R[_] += Q[_].c;
}
LL ans = 0LL;
for (int _=x;_<=n-y;_++) ans = max(ans, L[_]+R[_+1]);
cout << ans << endl;
return 0;
}
D - Tree and Hamilton Path
题意:
给定一个
N
个节点的带权树,定义图上
1≤Ai<Bi≤N
给定的图是一棵树
1≤Ci≤108
读入的数都是整数
解答:
一条边最后贡献答案的次数,至多是两倍拿掉该条边后的两个连通块中较小的那个连通块大小,记这个值为
S
。
由子树大小,联想到树的重心,分两种情况。
树的重心,即树上的一个点,使得拿掉这个点之后树上最大连通块的大小
- 树只存在一个重心。这条哈密尔顿路径的两端至少有一端不为该树的重心,则重心到连向该点所在子树的根这一条边是无法达到它所能经过最多次数的上限的。若令哈密尔顿路径起点为重心,终点为该子树的根,是可以构造一条合法的哈密尔顿路径达到另外所有边的上限的。(即在各个子树间来回跳)。选取一条连接重心权值最小的边的两端作为起点和终点即可。
最终答案为 S−min( 连接重心的一条边的边权$) - 树存在两个重心。连接这两个重心的那一条边,至多可以经过
n−1
次。(同样考虑哈密尔顿路径的起点和终点),同样我们以这条边的两个端点分别作为哈密尔顿路径的起点和终点,在两个子树间来回跳即可达到最大值。
最终答案为 S−( 连接两个重心那条边的权值 )
综上所述,即重心一定要作为哈密尔顿路径的起点或者终点。
通过树上
#include <bits/stdc++.h>
#define N 1000500
using namespace std;
typedef long long LL;
inline int rd() {int r;scanf("%d",&r);return r;}
struct Edge{int b,v,n;}e[2*N];
int siz[N],h[N],cnt,mark,g,n;
LL ans;
void link(int a,int b,int v) {
e[++cnt] = (Edge){b,v,h[a]}, h[a] = cnt;
}
void dfs(int u,int f,int p) {
siz[u] = 1;
int mx = 0;
for (int i=h[u];i;i=e[i].n) {
int v = e[i].b, cp = e[i].v;
if (v == f) continue;
dfs(v, u, cp);
siz[u] += siz[v];
mx = max(mx, siz[v]);
}
mx = max(mx, n-siz[u]);
if (siz[u]*2 == n) mark = p;
if (mx*2 <= n) g = u;
ans += 2LL * p * min(siz[u], n-siz[u]);
}
int main() {
n = rd();
for (int i=1;i<n;i++) {
int a = rd(), b = rd(), c = rd();
link(a, b, c);
link(b, a, c);
}
dfs(1,1,0);
if (!mark) {
mark = 2147483647;
for (int i=h[g];i;i=e[i].n)
mark = min(e[i].v, mark);
}
cout << ans - mark << endl;
return 0;
}
E - Sightseeing Plan
题意:
给定一个网格图,你需要制定一个旅行计划:
- 起点的横坐标在区间 [X1,X2] ,纵坐标在区间 [Y1,Y2]
- 途径一个特殊点,横坐标在区间 [X3,X4] , 纵坐标在区间 [Y3,Y4]
- 终点的横坐标在区间 [X5,X6] ,纵坐标在区间 [Y5,Y6]
求有多少条不同的最短路。
两条路径是不同的,当且仅当路径的起点不同,选择的特殊点不同,终点不同,或者途径的点不同。答案对
109+7
取模。
1≤X1≤X2<X3≤X4<X5≤X6≤106
1≤Y1≤Y2<Y3≤Y4<Y5≤Y6≤106
解答:
这题有点恶心,真的再也不想见到这种题。
定义
Fi,j
表示网格图上从点(1,1)走到点(i,j)的不同的最短路径个数
有
Fi,j=Cii+j
利用:
Fi,j=Fi−1,j+Fi,j−1
可以得出:
∑ix=1∑jy=1Fx,y=Fi+1,j+1−1
类比二维前缀和,可以将起点的区域转化成四个带权的单点
+1 : (x2+1,y2+1), (x1,y1)
-1 : (x1,y2+1), (x2+1,y1)
同理,也可以将终点的区域转化成四个带权的单点
原问题可以转化成求16个固定起点和终点,在固定区域
[x3,x4][y3,y4]
中选择一个中点,不同的最短路是多少
考虑这条路径进入固定区域的点以及离开固定区域的点,原问题路径按照进入固定区域的点和离开固定区域的点分类,最后答案乘上在固定区域内行走的路径长度,即为所求。
时空复杂度为
O(106)
,来源于组合数的预处理
#include <bits/stdc++.h>
#define mod 1000000007
#define N 2000500
using namespace std;
typedef long long LL;
LL Fac[N],Inv[N],Iac[N];
int x[7],y[7];
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
inline void inc(LL &x,LL y) {x=(x+y)%mod;}
inline LL C(int x,int y) {
return 1LL * Fac[x+y] * Iac[x] % mod * Iac[y] % mod;
}
inline LL W(int x1,int y1,int x2,int y2) {
x1 = abs(x1); y1=abs(y1); x2=abs(x2); y2=abs(y2);
return (1LL *C(x2+1,y2+1) - C(x2+1,y1) - C(x1,y2+1) + C(x1,y1)+mod + mod) % mod;
}
void init() {
for (int i=1;i<=6;i++) x[i] = rd();
for (int i=1;i<=6;i++) y[i] = rd();
}
void prepare() {
int n = 2000000;
Fac[0] = Fac[1] = 1;
for (int i=2;i<=n;i++) Fac[i] = 1LL * Fac[i-1] * i % mod;
Inv[0] = Inv[1] = 1;
for (int i=2;i<=n;i++) Inv[i] = 1LL * (mod - mod / i) * Inv[mod%i] % mod;
Iac[0] = Iac[1] = 1;
for (int i=2;i<=n;i++) Iac[i] = 1LL * Iac[i-1] * Inv[i] % mod;
}
void solve() {
LL ans = 0LL;
for (int _=x[3];_<=x[4];_++)
inc(ans, 1LL*(mod-_-y[3])*W(_-x[2],y[3]-1-y[2],_-x[1],y[3]-1-y[1])%mod * W(_-x[5],y[3]-y[5],_-x[6],y[3]-y[6])%mod);
for (int _=x[3];_<=x[4]; _++)
inc(ans, 1LL*(_+y[4]+1)*W(_-x[2],y[4]-y[2],_-x[1],y[4]-y[1])%mod*W(_-x[5],y[4]-y[5]+1,_-x[6],y[4]-y[6]+1)%mod);
for (int _=y[3];_<=y[4];_++)
inc(ans, 1LL*(mod-_-x[3])*W(x[3]-1-x[2],_-y[2],x[3]-1-x[1],_-y[1])%mod *W(x[3]-x[5], _-y[5], x[3]-x[6], _-y[6])%mod);
for (int _=y[3];_<=y[4];_++)
inc(ans, 1LL*(_+x[4]+1)*W(x[4]-x[2],_-y[2],x[4]-x[1],_-y[1])%mod *W(x[4]-x[5]+1,_-y[5],x[4]-x[6]+1,_-y[6])%mod);
cout<<ans<<endl;
}
int main() {
init();
prepare();
solve();
return 0;
}