2020 CCPC Wannafly Winter Camp Day1

PTA
牛客

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\)个,那么就有:

\[g(n,m)={n\choose 2}-\sum_{i=1}^{m}{c_i\choose 2} \]

观察上式可知,\(c_i\)均分答案最优,那么就有\((m-n\%m)\)\(\lfloor\frac{n}{m}\rfloor\)\((n\%m)\)\(\lceil\frac{n}{m}\rceil\)
所以上式可写为:

\[\begin{aligned} g(n,m)=&{n\choose 2}-\sum_{i=1}^{m}{c_i\choose 2}\\ =&{n\choose 2}-(m-n\%m){\lfloor\frac{n}{m}\rfloor\choose 2}-(n\%m){\lceil\frac{n}{m}\rceil\choose 2}\\ =&{n\choose 2}-(m-n\%m){\lfloor\frac{n}{m}\rfloor\choose 2}-(n\%m){\lfloor\frac{n-1}{m}\rfloor+1\choose 2} \end{aligned} \]

\(\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)

posted @ 2020-02-04 19:27  heyuhhh  阅读(350)  评论(0编辑  收藏  举报