2017中国大学生程序设计竞赛-哈尔滨站

目录
  • Contest Info
  • Solutions
    • A - Palindrome
    • B - K-th Number
    • D - X-Men
    • F - Permutation
    • H - A Simple Stone Game
    • K - Server
    • L - Color a Tree

Contest Info


传送门

Solved A B C D E F G H I J K L M
8 / 13 O O - Ø - O - O - - Ø Ø Ø
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A - Palindrome

可以将问题转化为区间覆盖问题,一般这种可以直接通过主席树解决。
但这里我们按照区间右端点升序排序过后可以直接维护一个树状数组来统计。

Code
#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ld;
const int MAXN = 5e5 + 5, MAXM = 4e5 + 5, BOUND = 2e5, MOD = 1e9+7, INF = 0x3f3f3f3f, base = 10000;
const int inv2 = (MOD + 1) >> 1;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define lc(x) ch[x][0]
#define pii pair
#define vi vector
#define vii vector>
#define rc(x) ch[x][1]
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define all(a) (a).begin(), (a).end()
#define sz(a) int(a.size())
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fi first
#define se second
#define MP std::make_pair
#define ri register int
//#define sz(a) int((a).size())
inline int add(int a, int b) {return a + b >= MOD ? a + b - MOD : a + b;}
inline int dec(int a, int b) {return a < b ? a - b + MOD : a - b;}
inline int mul(int a, int b) {return 1ll * a * b % MOD;}
template 
inline void cmin(T &a,T b){a = min(a,b);}
template 
inline void cmax(T &a,T b){a = max(a,b);}
ll qpow(ll a,ll b){
    ll ans=1;
    for(;b;b>>=1,a=a*a%MOD)if(b&1)ans=ans*a%MOD;
    return ans;
}
mt19937 mrand(random_device{}());
char s[MAXN*2],t[MAXN];
int p[MAXN*2],n;
void manacher(){
	s[0]=s[1]='#';
	for(int i=1;i<=n;i++){
		s[i*2] = t[i];
		s[i*2+1]='#';
	}
	int _n=n*2+1;
	int m,mx=0;
	for(int i=_n+1;!s[i];i++)s[i]='0';
	for(int i=1;i<=_n;i++){
		if(mx>i)p[i]=min(p[2*m-i],p[m]+m-i);
	else p[i]=1;
	for(;s[i+p[i]]==s[i-p[i]];++p[i]);
		if(p[i]+i>mx)mx=p[i]+i,m=i;
	}
}
int bit[MAXN];
void upd(int x,int v){
    for(;x;x-=x&-x)bit[x]+=v;
}
int que(int x){
    int ans=0;
    for(;x<=n;x+=x&-x)ans += bit[x];
    return ans;
}

priority_queue q;
void run(){
    cin>>(t+1);
    n=strlen(t+1);
    rep(i,0,n)bit[i]=0;
    while(!q.empty())q.pop();
    manacher();
    ll ans=0;
    rep(i,1,n){
        while(!q.empty() && -(q.top().fi)>_;
    while(_--)run();
    return 0;
}

B - K-th Number

二分然后check即可。

Code
#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ld;
const int MAXN = 1e5 + 5, MAXM = 4e5 + 5, BOUND = 2e5, MOD = 1e9+7, INF = 0x3f3f3f3f, base = 10000;
const int inv2 = (MOD + 1) >> 1;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define lc(x) ch[x][0]
#define pii pair
#define vi vector
#define vii vector>
#define rc(x) ch[x][1]
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define all(a) (a).begin(), (a).end()
#define sz(a) int(a.size())
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fi first
#define se second
#define MP std::make_pair
#define ri register int
//#define sz(a) int((a).size())
inline int add(int a, int b) {return a + b >= MOD ? a + b - MOD : a + b;}
inline int dec(int a, int b) {return a < b ? a - b + MOD : a - b;}
inline int mul(int a, int b) {return 1ll * a * b % MOD;}
template 
inline void cmin(T &a,T b){a = min(a,b);}
template 
inline void cmax(T &a,T b){a = max(a,b);}
ll qpow(ll a,ll b){
    ll ans=1;
    for(;b;b>>=1,a=a*a%MOD)if(b&1)ans=ans*a%MOD;
    return ans;
}
mt19937 mrand(random_device{}());
int n,k,a[MAXN];
ll M;
bool check(int x){
    int sum=0;
    ll cnt=0;
    for(int l=1,r=0;l<=n;sum-=(a[l++]>=x)){
        while(r+1<=n && sum=x);
        if(sum>=k)cnt += n-r+1;
    }
    return cnt>=M;
}
void run(){
    cin>>n>>k>>M;
    rep(i,1,n)cin>>a[i];
    
    int l=1,r=1e9,m;
    while(l>_;
    while(_--)run();
    return 0;
}

D - X-Men

每次距离最远的两个人必定距离会减少\(2\),所以答案很容易求,求一个直径就行。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/16 15:45:05
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#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 << std::endl; }
  template
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template  class T, typename t, typename... A> 
  void err(const T  &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair pii;
//head
const int N = 1e3 + 5;

int n, m;
vector  G[N];
int a[N];

int Max, rt;
void dfs(int u, int fa, int d) {
    if (a[u]) {
        if (d > Max) {
            Max = d;
            rt = u;
        }
    }
    for (auto v : G[u]) if (v != fa) {
        dfs(v, u, d + 1);
    }
}

void run() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        a[i] = 0;
        G[i].clear();
    }
    for (int i = 1; i <= m; i++) {
        int x; cin >> x;
        a[x] = 1;
    }
    for (int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    Max = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i]) {
            dfs(i, 0, 0);
            Max = 0;
            dfs(rt, 0, 0);
            break;
        }
    }
    cout << (double)(Max / 2) << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(2);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

F - Permutation

签到。直接构造。

Code
#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ld;
const int MAXN = 1e5 + 5, MAXM = 4e5 + 5, BOUND = 2e5, MOD = 1e9+7, INF = 0x3f3f3f3f, base = 10000;
const int inv2 = (MOD + 1) >> 1;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define lc(x) ch[x][0]
#define pii pair
#define vi vector
#define vii vector>
#define rc(x) ch[x][1]
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define all(a) (a).begin(), (a).end()
#define sz(a) int(a.size())
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fi first
#define se second
#define MP std::make_pair
#define ri register int
//#define sz(a) int((a).size())
const int N = 2e5,M = (1<<20);
inline int add(int a, int b) {return a + b >= MOD ? a + b - MOD : a + b;}
inline int dec(int a, int b) {return a < b ? a - b + MOD : a - b;}
inline int mul(int a, int b) {return 1ll * a * b % MOD;}
template 
inline void cmin(T &a,T b){a = min(a,b);}
template 
inline void cmax(T &a,T b){a = max(a,b);}
ll qpow(ll a,ll b){
    ll ans=1;
    for(;b;b>>=1,a=a*a%MOD)if(b&1)ans=ans*a%MOD;
    return ans;
}
mt19937 mrand(random_device{}());
int ans[MAXN];
void run(){
    int n;cin>>n;
    rep(i,1,(n+1)/2){
        ans[i*2-1] = i;
    }
    int j=1;
    rep(i,(n+1)/2+1,n){
        ans[j*2] = i;
        j++;
    }
    rep(i,1,n)cout<>_;
    while(_--)run();
    return 0;
}

H - A Simple Stone Game

题意:
\(n\)堆石子,每堆\(a_i\)个石头,每次操作可以移动一个石头从一堆到另外一堆。现在使得所有石堆石子个数都为某个\(x\)的倍数,问最少移动多少次。

思路:
考虑比较暴力的做法,枚举每个\(x\),那么每个数都会变为其倍数,之后在模\(x\)意义下处理一下即可。
不考虑枚举的复杂度,直接处理复杂度是\(O(nlogn)\)的,直接枚举显然不行。
注意到我们其实并没有必要枚举\(x\),而枚举最小质因子\(p\),因为当所有的数都为\(x\)倍数时,一定也为\(p\)的倍数。那么我们枚举时即可节约很多次枚举。
因为\(n\leq 10^5,a_i\leq 10^5\),质因子的范围为\([2,10^10]\),但考虑连续\(10\)个左右的质因子乘积会超过\(10^10\),所以有用的质因子不会超过\(10\)个的样子。复杂度就很棒。

Code
#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ld;
const int MAXN = 1e5 + 5, MAXM = 4e5 + 5, BOUND = 2e5, MOD = 1e9+7, INF = 0x3f3f3f3f, base = 10000;
const int inv2 = (MOD + 1) >> 1;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define lc(x) ch[x][0]
#define pii pair
#define vi vector
#define vii vector>
#define rc(x) ch[x][1]
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define all(a) (a).begin(), (a).end()
#define sz(a) int(a.size())
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fi first
#define se second
#define MP std::make_pair
#define ri register int
//#define sz(a) int((a).size())
inline int add(int a, int b) {return a + b >= MOD ? a + b - MOD : a + b;}
inline int dec(int a, int b) {return a < b ? a - b + MOD : a - b;}
inline int mul(int a, int b) {return 1ll * a * b % MOD;}
template 
inline void cmin(T &a,T b){a = min(a,b);}
template 
inline void cmax(T &a,T b){a = max(a,b);}
ll qpow(ll a,ll b){
    ll ans=1;
    for(;b;b>>=1,a=a*a%MOD)if(b&1)ans=ans*a%MOD;
    return ans;
}
mt19937 mrand(random_device{}());
int primes[MAXN],cnt;
bool is_prime[MAXN];
void init(){
    is_prime[1] = 1;
    for(int i=2;i<=100000;i++){
        if(!is_prime[i])primes[cnt++]=i;
        for(int j=0;j> q;
void run(){
    int n;cin>>n;
    ll sum=0;
    rep(i,1,n)cin>>a[i],sum+=a[i];
    ll ans=INFL;
    for(ll i=2;i<=sqrt(sum+0.5);i++){
        if(sum%i==0){
            while(sum%i==0){
                sum/=i;
            }
            ll sum2=0;
            rep(j,1,n){
                sum2 += a[j]%i;
            }
            int cnt = sum2/i;
            rep(j,1,n){
                if(a[j]%i)q.push(a[j]%i);
                if(int(q.size())>cnt)q.pop();
            }
            while(!q.empty()){
                int t=q.top();q.pop();
                sum2-=t;
            }
            ans = min(ans,sum2);
        }
    }
    if(sum>1){
        ll sum2=0;
        rep(j,1,n){
            sum2 += a[j]%sum;
        }
        int cnt = sum2/sum;
        rep(j,1,n){
            if(a[j]%sum)q.push(a[j]%sum);
            if(int(q.size())>cnt)q.pop();
        }
        while(!q.empty()){
            int t=q.top();q.pop();
            sum2-=t;
        }
        ans = min(ans,sum2);
    }
    cout<>_;
    while(_--)run();
    return 0;
}

K - Server

题意:
给出若干段带有价值的区间,现在使得\([1,n]\)每个整数点都被覆盖,求最小价值。区间价值可能为负数。

思路:
给出的题意其实是转化过后的题意,原题意很容易发现是个分数规划的模板,所以套个分数规划题意就转变过来了。
这其实可以直接通过\(dp\)+线段树来维护,时间复杂度为\(O(nlogn)\)。不过这个题有点卡常,我们可以通过树状数组来维护。
树状数组的话需要维护区间最小值、单点更新。发现普通的树状数组并不好维护。假设我们枚举到了\(i\),我们发现我们始终需要知道的是\([x,i]\)这种形式的区间最小值,但树状数组维护前缀最小值是很在行的!我们可以将这个区间变化一下:\([x,i]\rightarrow [x,n]\)。是不是一下就很好解决了!因为\([i,n]\)这段区间始终是\(0\),算上也没啥影响。那么这个题就相当于维护一个后缀最小值。至于单点更新嘛也很简单,类似于前缀那样就行。
注意有个细节,就是区间价值为负的区间,不管怎么我们都要选上的,这里没注意会wa,因为有些\(dp\)处理不了的情况,这时贪心处理即可。
细节见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/15 16:24:12
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair pii;
//head
const int N = 1e5 + 5;

int n, t;
int a[N], b[N];
vector  v[N];

ll c[N];

struct BIT {
    ll c[N];
    int lowbit(int x) {
        return x & -x;
    }
    void update(int x, ll v) {
        ++x;
        x = t + 1 - x + 1;
        for (; x <= t + 1; x += lowbit(x)) {
            c[x] = min(c[x], v);
        }
    }
    ll query(int x) {
        ++x;
        x = t + 1 - x + 1;
        ll res = 1e18;
        for (; x; x -= lowbit(x)) {
            res = min(res, c[x]);
        }
        return res;
    }
}bit;

ll check(int x) {
    ll res = 0;
    for (int i = 1; i <= n; i++) {
        c[i] = a[i] - 1ll * b[i] * x;
    }
    for (int i = 0; i <= t + 1; i++) {
        bit.c[i] = 1e18;
    }
    bit.update(0, 0);
    for (int i = 1; i <= t; i++) {
        for (auto it : v[i]) {
            int l = it.fi, r = i, id = it.se;
            ll now = bit.query(l - 1);
            if (c[id] < 0) res -= c[id], c[id] = 0;
            bit.update(i, now + c[id]);
        }
    }
    return bit.query(t) <= res;
}


void run() {
    scanf("%d%d", &n, &t);
    for (int i = 0; i <= t; i++) {
        v[t].clear();
    }
    for (int i = 1; i <= n; i++) {
        int l, r; scanf("%d%d", &l, &r);
        v[r].emplace_back(MP(l, i));
        scanf("%d%d", &a[i], &b[i]);
        a[i] *= 10000;
    }
    int l = 0, r = 1e7 + 5, mid;
    while (l < r) {
        mid = (l + r) >> 1;
        if (check(mid)) {
            r = mid;
        } else {
            l = mid + 1;
        }
    }
    double ans = 1.0 * r / 10000;
    printf("%.3f\n", ans);
}

int main() {
    int T; scanf("%d", &T); while(T--)
    run();
    return 0;
}

L - Color a Tree

题意:
\(A+B,A,B\leq 10^5\)个限制,其中\(A\)个限制为每个点子树内至少有多少个染色点,\(B\)个限制为每个点子树外至少有多少个点。
问最少染色多少个点,使得能够满足所有的限制。

思路:
注意到染色点越多我们越能够满足所有的限制,所以这里有个单调性。那么我们可以对染色点数\(x\)二分来check。
子树内外这个问题不好解决。子树内倒好解决,那子树外呢。子树内是一个至少的问题,其实对子树外,就是子树内一个至多的问题。两个关系有一定联系。
ok,这两点想通了这个题就很简单了,直接树\(dp\)来check一下就行。
当然会有一些判定上面的细节,比如我们求\(least[u]\)时应该是能不加就不加,对于\(most[u]\)能加上就加上。另外还有一些判定。
细节见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/16 9:21:10
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#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 << std::endl; }
  template
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template  class T, typename t, typename... A> 
  void err(const T  &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair pii;
//head
const int N = 1e5 + 5;

int n;
vector  G[N];
int in[N], out[N];
int least[N], most[N];
int sz[N];

void dfs0(int u, int fa) {
    sz[u] = 1;
    for (auto v : G[u]) if (v != fa) {
        dfs0(v, u);
        sz[u] += sz[v];
    }
}

void dfs(int u, int fa) {
    int all1 = 0, all2 = 0;
    for (auto v : G[u]) if (v != fa) {
        dfs(v, u);
        all1 += least[v];
        all2 += most[v];
    }
    least[u] = max(least[u], all1);
    most[u] = min(most[u], all2 + (most[u] != 0));
}

bool check(int x) {
    for (int i = 1; i <= n; i++) {
        if (in[i] != -1) {
            least[i] = in[i];
        } else {
            least[i] = 0;
        }
        if (out[i] != -1) {
            most[i] = x - out[i];
        } else {
            most[i] = sz[i];
        }
        most[i] = min(most[i], sz[i]);
    }
    dfs(1, 0);
    for (int i = 1; i <= n; i++) {
        most[i] = min(most[i], sz[i]);
        if (least[i] > most[i]) {
            return false;
        }
    }
    return least[1] <= x && x <= most[1];
}

void run() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        G[i].clear();
        in[i] = out[i] = -1;
    }
    for (int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs0(1, 0);
    int A; cin >> A;
    for (int i = 1; i <= A; i++) {
        int x, y; cin >> x >> y;
        in[x] = max(in[x], y);
    }
    int B; cin >> B;
    for (int i = 1; i <= B; i++) {
        int x, y; cin >> x >> y;
        out[x] = max(out[x], y);
    }
    for (int i = 1; i <= n; i++) {
        if (out[i] > n - sz[i] || in[i] > sz[i]) {
            cout << -1 << '\n';
            return;
        }
    }
    int l = 0, r = n + 1, mid;
    while (l < r) {
        mid = (l + r) >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    int ans = (r == n + 1 ? -1 : r);
    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;
}

你可能感兴趣的:(2017中国大学生程序设计竞赛-哈尔滨站)