2020 CCPC Wannafly Winter Camp Day1
A.期望逆序对
贪心考虑即可。
考虑最终序列的最优解,对于两个区间\([l_1,r_1],[l_2,r_2],l_1\leq l_2\),若:
- \(r_1\leq r_2\),那么直接按顺序摆放即可;
- \(r_1>r_2\),我们直接按照\(\frac{l+r}{2}\)的大小关系进行摆放。
综上,我们直接按照\(\frac{l+r}{2}\)排序即可。
这样即可得到最优排列。
之后\(O(n^2)\)枚举区间统计最终逆序对的期望个数即可。两个区间产生的贡献为\(O(1)\)段的分段函数,可以直接\(O(1)\)计算。
细节见代码:
Code
/*
* Author: heyuhhh
* Created Time: 2020/1/13 7:27:08
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5e3 + 5, MOD = 998244353;
struct seg {
int l, r;
bool operator < (const seg &A) const {
return 1.0 * (r + l) / 2 < 1.0 * (A.r + A.l) / 2;
}
}a[N];
int n;
int inv[N];
ll qpow(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) ans = ans * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return ans;
}
int sum(int a, int b) {
return 1ll * (a + b) * (b - a + 1) / 2 % MOD;
}
void run(){
for(int i = 1; i <= n; i++) {
cin >> a[i].l >> a[i].r;
}
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i++) {
inv[i] = qpow(a[i].r - a[i].l + 1, MOD - 2);
}
int ans = 0;
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
if(a[j].l >= a[i].r) continue;
if(a[j].l >= a[i].l) {
int res = sum(1, min(a[j].r, a[i].r) - a[j].l);
if(a[j].r < a[i].r) {
res = (res + 1ll * (a[i].r - a[j].r) * (a[j].r - a[j].l + 1) % MOD) % MOD;
}
res = 1ll * res * inv[i] % MOD * inv[j] % MOD;
ans = (ans + res) % MOD;
} else {
int res = sum(1, a[i].r - a[i].l);
res = (res + 1ll * (a[i].l - a[j].l) * (a[i].r - a[i].l + 1) % MOD) % MOD;
res = 1ll * res * inv[i] % MOD * inv[j] % MOD;
ans = (ans + res) % MOD;
}
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
while(cin >> n) run();
return 0;
}
B. 密码学
考虑逆过程即可。
Code
/*
* Author: heyuhhh
* Created Time: 2020/1/12 13:44:51
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1005, M = 105;
int n, m;
char s[N][M];
int x[N], y[N];
char trans(char a, char b) {
int x, y;
if(a <= 'Z') x = a - 'A' + 26;
else x = a - 'a';
if(b <= 'Z') y = b - 'A' + 26;
else y = b - 'a';
int z = (y - x + 52) % 52;
if(z <= 25) return z + 'a';
else return z - 26 + 'A';
}
void run(){
for(int i = 1; i <= m; i++) cin >> x[i] >> y[i];
for(int i = 1; i <= n; i++) cin >> (s[i] + 1);
for(int i = m; i >= 1; i--) {
int len = strlen(s[x[i]] + 1);
int len2 = strlen(s[y[i]] + 1);
for(int j = 1; j <= len2; j++) {
s[y[i]][j] = trans(s[x[i]][(j - 1) % len + 1], s[y[i]][j]);
}
}
for(int i = 1; i <= n; i++) {
cout << (s[i] + 1) << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
while(cin >> n >> m) run();
return 0;
}
C. 染色图
现在考虑如何求\(g(n,m)\)。
假设第\(i\)种颜色有\(c_i\)个,那么就有:
观察上式可知,\(c_i\)均分答案最优,那么就有\((m-n\%m)\)个\(\lfloor\frac{n}{m}\rfloor\),\((n\%m)\)个\(\lceil\frac{n}{m}\rceil\)。
所以上式可写为:
因\(\displaystyle n\%m=n-m\cdot\lfloor\frac{n}{m}\rfloor\),所以最终的答案直接整除分块处理即可。
Code
/*
* Author: heyuhhh
* Created Time: 2020/1/12 22:57:43
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 998244353;
int n, l, r;
int C(int n) {
return 1ll * n * (n - 1) / 2 % MOD;
}
int sum(int a, int b) {
return 1ll * (a + b) * (b - a + 1) / 2 % MOD;
}
void run() {
cin >> n >> l >> r;
int ans = 1ll * (r - l + 1) * C(n) % MOD, res = 0;
int rb = min(r, n - 1);
for(int i = l, j; i <= rb; i = j + 1) {
j = min(rb, min(n / (n / i), (n - 1) / ((n - 1) / i)));
res += (1ll * sum(i, j) * C(n / i) % MOD + 1ll * (1ll * n * (j - i + 1) % MOD
- 1ll * sum(i, j) * (n / i) % MOD + MOD) * (C((n - 1) / i + 1) - C(n / i) + MOD) % MOD) % MOD;
if(res >= MOD) res -= MOD;
}
ans -= res;
if(ans < 0) ans += MOD;
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T;
while(T--) run();
return 0;
}
E. 树与路径
类似于换根的思想,当以\(1\)为根节点时答案很显然,考虑如何换根。
假设我们现在得到\(i\)结点为根的答案\(A_i\),现在考虑\(A_{f_i}\)。显然我们只需要枚举每条路径然后看贡献:
- 当前路径不同时经过\(i,f_i\),那么贡献不会发生改变;
- 当前路径同时经过\(i,f_i\),若\(u\rightarrow i\)长度为\(x\),路径长度为\(l\),那么对\(i\)的贡献为\(x(l-x)\);同理对\(f_i\)的贡献为\((x+1)(l-x-1)\)。即对\(A_i-A_{f_i}\)的贡献为\(2x-l+1\)。这里的\(x\)只与两个点的深度有关,我们将其拆开就有贡献为:\(2(d_u-d_i)-l+1\)。
那么现在考虑路径\((u,v)\),令\(lca=LCA(u,v)\)。现在只考虑\((u,lca)\)这段,另外一半同理。那么可以用树上差分的技巧算出对路径上的点的贡献,即对每个点算出\(A_i-A_{f_i}\)。
然后\(dfs\)一遍求出以所有结点为根的答案即可。
代码如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/5 21:44:45
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 3e5 + 5;
int n, m;
int f[N][20], deep[N];
struct Edge {
int v, next;
}e[N << 1];
int head[N], tot;
void adde(int u, int v) {
e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
}
void dfs(int u, int fa) {
deep[u] = deep[fa] + 1;
f[u][0] = fa;
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v != fa) dfs(v, u);
}
}
int LCA(int x, int y) {
if(deep[x] < deep[y]) swap(x, y);
for(int i = 19; i >= 0; i--) {
if(deep[f[x][i]] >= deep[y]) x = f[x][i];
}
if(x == y) return x;
for(int i = 19; i >= 0; i--) {
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
}
return f[x][0];
}
ll ans[N], a[N], b[N];
void dfs2(int u, int fa) {
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v != fa) {
dfs2(v, u);
a[u] += a[v], b[u] += b[v];
}
}
}
void dfs3(int u, int fa) {
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v != fa) {
ans[v] = ans[u] + a[v] * (ll)deep[v] + b[v];
dfs3(v, u);
}
}
}
void run(){
memset(head, -1, sizeof(head));
cin >> n >> m;
for(int i = 1; i < n; i++) {
int u, v; cin >> u >> v;
adde(u, v); adde(v, u);
}
dfs(1, 0);
for(int i = 1; i < 20; i++) {
for(int j = 1; j <= n; j++) {
f[j][i] = f[f[j][i - 1]][i - 1];
}
}
for(int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
int lca = LCA(u, v);
int l = deep[u] + deep[v] - 2 * deep[lca], x = deep[u] - deep[lca];
a[u] -= 2, a[v] -= 2, a[lca] += 4;
b[u] += 2 * deep[u] - l + 1;
b[v] += 2 * deep[v] - l + 1;
b[lca] -= 2 * deep[u] + 2 * deep[v] - 2 * l + 2;
ans[1] += 1ll * x * (l - x);
}
dfs2(1, 0);
dfs3(1, 0);
for(int i = 1; i <= n; i++) cout << ans[i] << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
F. 乘法
易知\(A,B\)元素的顺序不影响最终答案,所以可以先对其排序。
然后直接二分第\(k\)大元素,枚举\(A\)中的所有元素,在\(B\)中二分值,统计有多少个元素比二分值小即可。
有许多细节需要考虑,比如\(0\)和正负数上下取整之类的。
细节见代码:
Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e5+5,MAXM = 1e6+5,MOD = 2008,INF = 0x3f3f3f3f,N=2e5;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-7;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
#define x first
#define y second
using namespace std;
int n,m;
ll k;
vi a1,a2,b1,b2;
ll check1(ll m){
ll ans=0;
for(int x:a1){
auto it = lower_bound(b2.begin(),b2.end(),m/x);
if(it!=b2.end())ans += b2.size() - (it - b2.begin());
}
for(int x:a2){
auto it = upper_bound(b1.begin(),b1.end(),m/x);
if(it!=b1.begin()){
ans += it-b1.begin();
}
}
return ans;
}
ll check2(ll m){
ll ans=0;
for(int x:a1){
auto it = lower_bound(b1.begin(),b1.end(),m/x);
if(it!=b1.end())ans += b1.size() - (it - b1.begin());
}
//if(m==10)cout<<ans<<'\n';
for(int x:a2){
auto it = upper_bound(b2.begin(),b2.end(),m/x);
if(it!=b2.begin()){
ans += it-b2.begin();
}
//if(m==10)cout<<ans<<'\n';
}
//if(m==10)cout<<ans<<'\n';
return ans;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cin>>n>>m>>k;
int x;
for(int i=1;i<=n;i++){
cin>>x;
if(x<0)a1.push_back(x);
else if(x>0)a2.push_back(x);
}
for(int i=1;i<=m;i++){
cin>>x;
if(x<0)b1.push_back(x);
else if(x>0)b2.push_back(x);
}
sort(a1.begin(),a1.end());
sort(a2.begin(),a2.end());
sort(b1.begin(),b1.end());
sort(b2.begin(),b2.end());
ll s1 = (ll)a1.size() * b2.size() + (ll)a2.size() * b1.size();
ll s2 = (ll)a1.size() * b1.size() + (ll)a2.size() * b2.size();
k = (ll)n*m - k + 1;
if(k<=s1){
ll l=-1e12,r=0,m;
while(l<r){
m = (l+r)>>1;
if(check1(m)>=k)r=m;
else l = m + 1;
}
cout<<l<<'\n';
}else if(k<=(ll)n*m -s2){
cout<<"0\n";
}else{
k -= (ll)n*m - s2;
ll l=1,r=1e12,m;
while(l<r){
m = (l+r)>>1;
if(check2(m)>=k)r=m;
else l = m + 1;
}
cout<<l<<'\n';
}
return 0;
}
G. 圆凸包
计算几何题。
Code
#include<cstdio>
#include<cmath>
#include<algorithm>
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define REPE(i,a,b) for(int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(int i=(a); i>=(b); i--)
#ifdef sahdsg
#define DBG(...) printf(__VA_ARGS__)
#else
#define DBG(...) (void)0
#endif
using namespace std;
#define D Point
#define CD const D
#define EPS 1e-8
int dcmp(double x) {return fabs(x)<EPS?0:(x<0?-1:1);}
struct D {
double x,y; int f; double ang;
void print() {DBG("%.10lf %.10lf\n", x, y);}
};
bool operator<(CD&l, CD&r) {return dcmp(l.x-r.x)<0 || (dcmp(l.x-r.x)==0 && dcmp(l.y-r.y)<0);}
bool operator==(CD&l, CD&r) {return dcmp(l.x-r.x)==0 && dcmp(l.y-r.y)==0;}
D operator-(CD&l, CD&r) {return (D){l.x-r.x, l.y-r.y,0,0};}
double dot(CD&l, CD&r) {return l.x*r.x+l.y*r.y;}
double cross(CD&l, CD&r) {return l.x*r.y-l.y*r.x;}
double len(CD&l) {return sqrt(l.x*l.x+l.y*l.y);}
double ang(CD&l) {return atan2(l.y,l.x);}
#define MAXN 107
#define LS (MAXN*MAXN*MAXN)
#define C Circle
#define CC const C
struct C {
D o; double r;
D point(double a) {return (D){o.x+cos(a)*r, o.y+sin(a)*r,-1,a};}
void read() {scanf("%lf%lf%lf", &o.x, &o.y, &r);}
void print() {DBG("%.0lf %.0lf %.0lf\n", o.x, o.y, r);}
};
bool operator<(CC&l, CC&r) {
return l.r<r.r;
}
bool operator==(CC&l, CC&r) {return dcmp(l.r-r.r)==0 && l.o==r.o;}
void get2Ctan(C a, C b, D*c, int&cnt, int i, int j) {
double dr=a.r-b.r;
if(dcmp(dr)<0) {swap(a,b); swap(i,j); dr=-dr;}
double t=len(a.o-b.o);
if(dcmp(t-dr)<=0) return;
double base = ang(b.o-a.o);
double ang;
if(dcmp(dr)!=0) {
ang=acos(dr/t);
} else {
ang=M_PI/2;
}
c[cnt]=a.point(base+ang); c[cnt].f=i; cnt++;
c[cnt]=b.point(base+ang); c[cnt].f=j; cnt++;
c[cnt]=a.point(base-ang); c[cnt].f=i; cnt++;
c[cnt]=b.point(base-ang); c[cnt].f=j; cnt++;
}
template<class T, int Z>
struct Arr {
T data[Z]; int n;
Arr():n(0){};
T&operator[](int z) {return data[z];}
void push(const T&x) {data[n++]=x;}
void pop() {n--;}
};
typedef Arr<D,LS> Plg;
void tubao(Plg &p, Plg &ch) {
sort(p.data,p.data+p.n);
p.n = unique(p.data,p.data+p.n)-p.data;
int &m = ch.n; m=0;
REP(i,0,p.n) {
while(m>1 && cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
ch[m++]=p[i];
}
int k=m;
PERE(i,p.n-2,0) {
while(m>k && cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
ch[m++]=p[i];
}
if(p.n>1) m--;
}
double normal(double r) {
return r-2*M_PI*floor(r/2/M_PI);
}
#undef C
#undef CD
#undef D
#undef CD
Circle cs[MAXN];
Point lsl[LS]; int cnt;
Plg p,ko;
int main() {
int t;
scanf("%d", &t);
while(0<t--) {
cnt = 0;
int n; scanf("%d", &n);
REP(i,0,n) cs[i].read();
sort(cs,cs+n); //n=unique(cs,cs+n)-cs;
int nn=n; n=0;
REP(i,0,nn) {
bool f=true;
REP(j,i+1,nn) {
double dr=cs[j].r-cs[i].r;
double dl=len(cs[i].o-cs[j].o);
if(dcmp(dl-dr)<=0) {
f=false;
break;
}
}
if(f) {
cs[n++]=cs[i];
}
}
if(n==1) {
printf("%.10lf\n", cs[0].r*2*M_PI);
continue;
}
REP(i,0,n) REP(j,i+1,n) {
get2Ctan(cs[i], cs[j], lsl, cnt, i, j);
}
p.n=0;
REP(i,0,cnt) {
bool f=true;
REP(j,0,n) {
if(dcmp(len(lsl[i]-cs[j].o)-cs[j].r)<0) {
f=false;
break;
}
}
if(f) p.push(lsl[i]);
}
tubao(p,ko);
ko[ko.n]=ko[0];
double ans=0;
//DBG("ko.n-> %d\n", ko.n);
REP(i,0,ko.n) {
//DBG("%d ", ko[i].f);
//ko[i].print();
if(ko[i].f == ko[i+1].f) {
double an = normal(ko[i+1].ang-ko[i].ang);
//DBG("@an=%lf\n", an);
ans += an * cs[ko[i].f].r;
} else {
ans += len(ko[i]-ko[i+1]);
}
}
printf("%.10lf\n", ans);
}
return 0;
}
H. 最大公约数
将题意转化一下,为:给出\(n,k\),找到最小的正整数\(y\),使得\(gcd(k,y)\not ={gcd(t,y)},1\leq t\leq n,t\not ={k}\)。
显然\(y\geq k\),我们令\(y=k\)。注意到若存在一个素数\(p,p\cdot k\leq n\),那么我们需要让\(y\)乘上一个\(p\)来区分\(k\)和\(p^s\cdot k,s>0\)。
对于\(1\)~\(n\)中所有的素数都这样搞一遍即可。
最后可能会爆\(long\ long\),所以需要高精或者python。
Code
def isPrime(x):
for i in range(2, x+1):
if i * i > x:
return True
if x % i == 0:
return False
if __name__ == '__main__':
primes = []
for i in range(2, 500):
if(isPrime(i)):
primes.append(i)
#print(primes)
T = int(input())
for i in range(T):
s = input()
n, k = map(int, s.split())
y = 1
for p in primes:
if p * k > n:
break
y = y*p
print(y*k)
重要的是自信,一旦有了自信,人就会赢得一切。