【AtCoder】AGC018
A - Getting Difference
我们肯定可以得到这些数的gcd,然后判断每个数减整数倍的gcd能否得到K
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int N,K;
int a[MAXN],g;
int gcd(int a,int b) {
return b == 0 ? a : gcd(b,a % b);
}
void Solve() {
read(N);read(K);
for(int i = 1 ; i <= N ; ++i) {
read(a[i]);
g = gcd(a[i],g);
}
for(int i = 1 ; i <= N ; ++i) {
if(a[i] >= K && (a[i] - K) % g == 0) {
puts("POSSIBLE");
return;
}
}
puts("IMPOSSIBLE");
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}
B - Sports Festival
对于初始的喜爱度答案,如果我们不把含有个数最多的比赛ban掉,答案永远不会下降,所以我们试试一次次ban掉人数最多的比赛,取每次最小值
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int N,M;
int a[305][305],ans;
int cnt[305],pos[305];
bool vis[305];
void Solve() {
read(N);read(M);
for(int i = 1 ; i <= N ; ++i) {
for(int j = 1 ; j <= M ; ++j) {
read(a[i][j]);
}
}
for(int i = 1 ; i <= N ; ++i) {cnt[a[i][1]]++;pos[i] = 1;}
for(int i = 1 ; i <= M ; ++i) ans = max(ans,cnt[i]);
for(int k = 1 ; k < M ; ++k) {
int t = 1;
for(int j = 1 ; j <= M ; ++j) {
if(cnt[j] > cnt[t]) t = j;
}
vis[t] = 1;
for(int i = 1 ; i <= N ; ++i) {
cnt[a[i][pos[i]]]--;
while(vis[a[i][pos[i]]]) {++pos[i];}
cnt[a[i][pos[i]]]++;
}
int tmp = 0;
for(int j = 1 ; j <= M ; ++j) {
tmp = max(tmp,cnt[j]);
}
ans = min(ans,tmp);
}
out(ans);enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}
C - Coins
把每个金币变成\((A_{i} - C_{i},B_{i} - C{i},0)\),然后总贡献加上所有\(C[i]\)
按照\(A[i] - B[i]\)排序,然后枚举一个中间点K,在K里面选\(B[i] - C[i]\)最大的Y个,在后面选\(A[i] - C[i]\)最大的\(X\)个,取最小值
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int X,Y,Z;
int64 A[MAXN],B[MAXN],C[MAXN],ans,f[MAXN],b[MAXN];
int id[MAXN];
multiset<int64> S;
void Solve() {
read(X);read(Y);read(Z);
for(int i = 1 ; i <= X + Y + Z ; ++i) {
read(A[i]);read(B[i]);read(C[i]);
ans += C[i];
A[i] -= C[i];B[i] -= C[i];
id[i] = i;
}
sort(id + 1,id + X + Y + Z + 1,[](int a,int b){return A[a] - B[a] < A[b] - B[b];});
int64 all = 0;
for(int i = 1 ; i <= X + Y + Z ; ++i) {
S.insert(B[id[i]]);
all += B[id[i]];
if(S.size() > Y) {
all -= *(S.begin());
S.erase(S.begin());
}
f[i] = all;
}
S.clear();all = 0;
for(int i = X + Y + Z ; i >= 1 ; --i) {
S.insert(A[id[i]]);
all += A[id[i]];
if(S.size() > X) {
all -= *(S.begin());
S.erase(S.begin());
}
b[i] = all;
}
int64 tmp = f[Y] + b[Y + 1];
for(int i = Y + 1; i <= Y + Z ; ++i) {
tmp = max(tmp,f[i] + b[i + 1]);
}
out(ans + tmp);enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}
D - Tree and Hamilton Path
熟练选手直接分析每条边能否取到最大贡献
每条边最大贡献是二倍的它断开后分成子树的较小的子树大小
如果是哈密顿回路,这个贡献显然可以通过求一个重心构造出来
哈密顿路分析一下,只和起点有关,和起点逐步往子树扩大的方向走,扩大到至少大小为\(\frac{N}{2}\)时所经过的边权和,就是我们要扣除的
如果有一条边可以把子树断成\(\frac{N}{2}\),那么减少的一定是这条边,否则其他情况都要经过这条边
否则求一个重心,找和重心相连的边最小的,其他情况一定包含一条和重心相连的边
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
struct node {
int to,next;int64 val;
}E[MAXN * 2];
int N,sumE,head[MAXN],siz[MAXN],son[MAXN],dep[MAXN],fa[MAXN];
int64 ans;
void add(int u,int v,int64 c) {
E[++sumE].to = v;
E[sumE].next = head[u];
E[sumE].val = c;
head[u] = sumE;
}
void dfs(int u) {
siz[u] = 1;
dep[u] = dep[fa[u]] + 1;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(v != fa[u]) {
fa[v] = u;
dfs(v);
siz[u] += siz[v];
son[u] = max(siz[v],son[u]);
ans += min(siz[v],N - siz[v]) * 2 * E[i].val;
}
}
son[u] = max(son[u],N - siz[u]);
}
void Solve() {
read(N);
int a,b;int64 c;
for(int i = 1 ; i < N ; ++i) {
read(a);read(b);read(c);
add(a,b,c);add(b,a,c);
}
dfs(1);
int t = 1;
for(int i = 2 ; i <= N ; ++i) {
if(son[i] < son[t] || (son[i] == son[t] && dep[i] > dep[t])) t = i;
}
if(N % 2 == 0 && son[t] == N / 2) {
for(int i = head[t] ; i ; i = E[i].next) {
if(E[i].to == fa[t]) ans -= E[i].val;
}
}
else {
int64 k = 0x7fffffff;
for(int i = head[t] ; i ; i = E[i].next) {
k = min(k,E[i].val);
}
ans -= k;
}
out(ans);enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}
E - Sightseeing Plan
问网格图中某一矩形区域到顶点的和
居然是可以\(O(1)\)统计的,感觉非常长知识
具体怎么\(O(1)\)呢
问\((0,0)\)点到\((x,y)\)点的方案数,是\(\binom{x + y}{y}\)
记为\(W(x,y)\)
就是\(\sum_{i = 0}^{y}W(x,i) = W(x + 1,y)\)
就是在选\(x,i\)往右走一步,然后就一直往上走
\(\sum_{i = 0}^{x}\sum_{j = 0}^{y} W(i,j) = W(x + 1,y + 1) - 1\)
因为横竖同理,我们可以把每个都合成\(W(i + 1,y)\),少一个\(W(0,y)\),所以-1
这样我们求一个区域的值
\(\sum_{i = x_{1}}^{x_{2}}\sum_{j = y_{1}}^{y_{2}} W(x_{2} + 1,y_{2} + 1) - W(x_{1},y_{2} + 1) - W(x{2} + 1,y_{2}) + W(x_{1},y_{1})\)
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 1000005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
const int MOD = 1000000007;
const int V = 2000005;
int X[7],Y[7],ans,fac[V + 5],invfac[V + 5];
int inc(int a,int b) {
return a + b >= MOD ? a + b - MOD : a + b;
}
int mul(int a,int b) {
return 1LL * a * b % MOD;
}
void update(int &x,int y) {
x = inc(x,y);
}
int fpow(int x,int c) {
int res = 1,t = x;
while(c) {
if(c & 1) res = mul(res,t);
t = mul(t,t);
c >>= 1;
}
return res;
}
int C(int n,int m) {
if(n < m) return 0;
return mul(fac[n],mul(invfac[m],invfac[n - m]));
}
int W(int x,int y) {
return C(x + y,x);
}
int way1(int x,int y) {
int s1 = x - X[2],s2 = x - X[1];
int t1 = y - Y[2],t2 = y - Y[1];
return inc(inc(W(s2 + 1,t2 + 1),W(s1,t1)),MOD - inc(W(s2 + 1,t1),W(s1,t2 + 1)));
}
int way2(int x,int y) {
int s1 = X[5] - x,s2 = X[6] - x;
int t1 = Y[5] - y,t2 = Y[6] - y;
return inc(inc(W(s2 + 1,t2 + 1),W(s1,t1)),MOD - inc(W(s2 + 1,t1),W(s1,t2 + 1)));
}
void Solve() {
fac[0] = 1;
for(int i = 1 ; i <= V ; ++i) fac[i] = mul(fac[i - 1],i);
invfac[V] = fpow(fac[V],MOD - 2);
for(int i = V - 1 ; i >= 0 ; --i) invfac[i] = mul(invfac[i + 1],i + 1);
for(int i = 1 ; i <= 6 ; ++i) read(X[i]);
for(int i = 1 ; i <= 6 ; ++i) read(Y[i]);
for(int i = X[3] ; i <= X[4] ; ++i) {
update(ans,mul(MOD - (i + Y[3] - 1),mul(way1(i,Y[3] - 1),way2(i,Y[3]))));
update(ans,mul(i + Y[4],mul(way1(i,Y[4]),way2(i,Y[4] + 1))));
}
for(int j = Y[3] ; j <= Y[4] ; ++j) {
update(ans,mul(MOD - (X[3] - 1 + j),mul(way1(X[3] - 1,j),way2(X[3],j))));
update(ans,mul(j + X[4],mul(way1(X[4],j),way2(X[4] + 1,j))));
}
out(ans);enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}
F - Two Trees
显然如果两个点在两棵树里儿子的奇偶性不同,一定误解
否则把两棵树建出一个无向图,然后如果两个点都有偶数个儿子,那么连一条边,再建一个点,把两棵树的树根都连在上面
求一个欧拉回路,那么如果从第一棵树到第二棵树,那么这个点就是1,否则就是-1
这样的道理是什么呢。。。可以从底向上归纳,显然两棵树的叶子点有两条边,出度和入度相等,给叶子上填上相应的数,然后叶子指向父亲的相当于把+1或-1贡献给父亲,从底向上归纳也行
在学完选修2-2之后,我可以把这个解释为,进行合情推理后,发现是对的= =
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define MAXN 100005
#define eps 1e-12
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef double db;
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int N;
int A[MAXN],B[MAXN],s[MAXN][2],ans[MAXN];
struct node {
int to,next;
}E[MAXN * 10];
int head[MAXN * 2],sumE = 1;
bool vis[MAXN * 10];
void add(int u,int v) {
E[++sumE].to = v;
E[sumE].next = head[u];
head[u] = sumE;
}
void euler_road(int u) {
for(int &i = head[u] ; i ; i = E[i].next) {
int w = i;
if(!vis[w]) {
vis[w] = vis[w ^ 1] = 1;
int v = E[w].to;
if(u != 2 * N + 1 && v != 2 * N + 1 && abs(u - v) == N) {
if(u < v) ans[u] = 1;
else ans[v] = -1;
}
euler_road(v);
}
}
}
void Solve() {
read(N);
for(int i = 1 ; i <= N ; ++i) {
read(A[i]);
if(A[i] != -1) {
add(i,A[i]);add(A[i],i);
s[A[i]][0]++;
}
else {
add(i,2 * N + 1);add(2 * N + 1,i);
}
}
for(int i = 1 ; i <= N ; ++i) {
read(B[i]);
if(B[i] != -1) {
add(i + N,B[i] + N);add(B[i] + N,i + N);
s[B[i]][1]++;
}
else {
add(i + N,2 * N + 1);add(2 * N + 1,i + N);
}
}
for(int i = 1 ; i <= N ; ++i) {
if((s[i][0] ^ s[i][1]) & 1) {
puts("IMPOSSIBLE");
return;
}
if(s[i][0] % 2 == 0) {
add(i,i + N);add(i + N,i);
}
}
puts("POSSIBLE");
euler_road(1);
for(int i = 1 ; i <= N ; ++i) {
out(ans[i]);space;
}
enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
}