11, 7 题解报告

T1 签到题

模拟就好。

code

#include<bits/stdc++.h>
using namespace std;

int read() {
 int x = 0, f = 1; char c = getchar();
 while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
 while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
 return x * f;
}
int n, now = 1, Ans;
priority_queue<int, vector<int>, greater<int> >q;
int main() {
  n = read();
  for (int i = 1; i <= n; i++) {
  	int x = read();
  	if(x == now) {
	  now = x + 1;
	  while(!q.empty() && (int)q.top() == now) {
	  	 now++;
	  	 q.pop();
	  }	 
	  continue;
	}
  	else {
  	  while(!q.empty() && (int)q.top() == now) {
	  	 now++;
	  	 q.pop();
	  }
	  if(now == x) 	Ans = max(Ans, (int)q.size());
  	  else {
  	    q.push(x);
  	    Ans = max(Ans, (int)q.size());
	  }
	}
  }
  cout<<Ans;
  return 0;
}

T2 Z^2∼N

打表找规律,然后大力分类讨论

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e5 + 5;
int read() {
 int x = 0, f = 1; char c = getchar();
 while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
 while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
 return x * f;
}
int n, m, a[MAXN];
struct node{int x, y;}b[MAXN];
signed main() {
  n = read(), m = read();
  for (int i = 1; i <= n; i++) {
  	int x = read(), y = read();
  	if(x == 0 && y == 0) a[i] = 0;
  	int ret = abs(x) + abs(y);
  	int cs = 1 + 2 * ret * (ret - 1);
    if (x > 0 && y == 0) a[i] = cs;
  	if(x == 0 && y > 0) a[i] = cs + 2 * (ret - 1) + 1;
  	int fx = cs + 2 * (ret - 1) + 1;
	if(x == 0 && y < 0) a[i] = cs + ret * 2 + (ret - 1) * 2 + 1;	 
	if(x > 0 && y > 0) a[i] = cs + (y - 1) * 2 + 1;
	if(x > 0 && y < 0) a[i] = cs + abs(y) * 2;
    if(x < 0 && y >= 0) a[i] = fx + abs(x);
    fx = fx + ret;
    if(x < 0 && y < 0) a[i] = fx + abs(y); 
  }
  for (int i = 1; i <= m; i++) {
  	int val = read();
  	if(val == 0) {b[i].x = 0, b[i].y = 0; continue;}
  	int y = sqrt((val - 1) / 2), ret;
  	ret = y;
  	for (int j = max(y - 1, 0ll); j <= y + 30; j++) {
  	  if (1 + 2 * j * (j - 1) <= val && 1 + 2 * j * (j + 1) > val) {ret = j; break;}	
	} 
	int cs = 1 + 2 * ret * (ret - 1);
    if(val == cs) b[i].x = ret, b[i].y = 0;
    if(val >= cs + 1 && val <= cs + 2 * (ret - 1)) {
	  int cz = val - cs;
	  if(cz & 1) {
	  	b[i].x = ret - (cz / 2 + 1);
	  	b[i].y = cz / 2 + 1;
	  } 
	  else {
	   	b[i].x = ret - cz / 2;
	  	b[i].y = (-1) * cz / 2;
	  }
	}
	if(val == cs + (ret - 1) * 2 + 1) b[i].x = 0, b[i].y = ret;
	int fx = cs + (ret - 1) * 2 + 1;
	if(val >= fx + 1 && val <= fx + ret) {
	   b[i].x = (-1) * (val - fx);
	   b[i].y = b[i].x + ret;
	}
	fx = fx + ret;
	if(val >= fx + 1 && val <= fx + (ret - 1)) {
		b[i].y = (val - fx) * (-1);
		b[i].x = (-ret) - b[i].y; 
	}
	if(val == fx + ret) {
		b[i].x = 0, b[i].y = (-1) * ret;
	}
  }
  for (int i = 1; i <= n; i++) cout<<a[i]<<"\n";
  for (int i = 1; i <= m; i++) cout<<b[i].x<<" "<<b[i].y<<"\n";
  return 0;
}

T3 P=NP

solution

稍微把那个取最大值的条件转化一下,其实就是一种颜色只能取一次最大值。

有一个朴素的状压 dp:对于每个点 \(i\) 和集合 \(S\),\(dp[i][S]\) 代表到达 \(i\),在 \(S\) 中的点取过颜色,得到的最大路径长度。转移就是对于每条边 \(u\to v\),分别考虑要不要取 \(v\) 的权值。如果要取,那么必须之前没取过,也就是对于不包含 \(c_v\) 的集合 \(S\) 有 \(dp[v][S\cup\{c_v\}]\leftarrow dp[u][S]+a_v\)。如果不取就随意了,转移是 \(dp[v][S]\leftarrow dp[u][S]\)。这样复杂度是 \(m2^n\)​。

然后你发现,如果一种颜色只出现了一次,显然没必要放到状压里,无脑取就行了

于是你只需要记出现至少两次的点,最多有 \(\frac n2\) 个。然后就做完了……时间复杂度 \(m2^{n/2}\)

code

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int read() {
 int x = 0, f = 1; char c = getchar();
 while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
 while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
 return x * f;
}
int n, m, cnt[40], f[37][(1 << 18)], id[40], tot, val[40], col[40];
vector<int>e[40];
int main() {
  n = read();
  for (int i = 1; i <= n; i++) {
    col[i] = read(), val[i] = read();
    cnt[col[i]]++;
    if(cnt[col[i]] == 2) id[col[i]] = ++tot;
  }
  m = read();
  for (int i = 1; i <= m; i++) {
    int u = read(), v = read();
    e[v].push_back(u);
  }
  memset(f, -0x3f, sizeof f);
  if(cnt[col[1]] == 1) f[1][0] = val[1];
  else {
    f[1][(1 << (id[col[1]] - 1))] = val[1];
    f[1][0] = 0;
  }
  for (int i = 2; i <= n; i++) {
    for (int k = 0; k < e[i].size(); k++) {
       int j = e[i][k];
       for (int S = 0; S < 1 << tot; S++) {
         if(cnt[col[i]] == 1) f[i][S] = max(f[i][S], f[j][S] + val[i]);
         else {
           f[i][S] = max(f[i][S], f[j][S]);
           int M = S | (1 << id[col[i]] - 1);
           if(!(S & (1 << id[col[i]] - 1))) f[i][M] = max(f[i][M], f[j][S] + val[i]);
         }
       }
    }
  }
  for (int i = 1; i <= n; i++) {
    int ans = *max_element(f[i], f[i] + (1 << tot));
    if(ans < 0) ans = 0;
    cout<<ans<<"\n";
  }
  system("pause");
  return 0;
}

T4 后缀数组

solution

考虑 SA 中相邻的元素,也就是排名相邻的两个串。如果你知道 Height,相当于你就知道了 \(h_i\) 对字符的相等关系,和末尾一对字符的小于关系。对于相等关系,可以用并查集合并。

如果你不知道 Height,那也可以通过判断它后面的字符串的大小关系确定能不能相等。即,对于后缀 \(x,y\),\(x\) 的字典序小于 \(y\)。如果 \(x+1\) 字典序小于 \(y+1\),那么 \(x,y\) 位置的字符可以取等,否则必须 \(s_x<s_y\)。判断 \(x+1,y+1\) 的字典序可以通过求 SA 的逆数组得到。

然后把这些小于关系在并查集合并后的点上连边,那实际上就是找一个字典序最小拓扑序。可以按照 SA 的顺序贪心,每个合并后的点放能放的最小值,这样一定是字典序最小的(因为每个字符都是最小的)

code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
#define X first
#define Y second
#define ALL(v) v.begin(), v.end()
#define pb push_back
#define SZ(a) ((int)a.size())
 
const int MAXn = 5005;
int sa[MAXn], hei[MAXn], rk[MAXn], val[MAXn];
 
 
int g[MAXn];
int f(int x) {
    return g[x] = (g[x] == x ? x : f(g[x]));
}
void mg(int a, int b) {
    a = f(a), b = f(b);
    g[a] = b;
}
 
vector<int> v[MAXn];
 
void add(int x, int y) {
    // x < y
    v[y].pb(x);
}
 
int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> sa[i], rk[sa[i]] = i;
    for (int i = 1; i < n; i++)
        cin >> hei[i];
    for (int i = 1; i <= n + 1; i++)
        g[i] = i;
    rk[n + 1] = 0;
    for (int i = 1; i < n; i++) {
        if (hei[i] != -1) {
            for (int j = 0; j < hei[i]; j++)
                mg(sa[i] + j, sa[i + 1] + j);
        }
    }
 
    for (int i = 1; i < n; i++) {
        if (hei[i] != -1) {
            add(f(sa[i] + hei[i]), f(sa[i + 1] + hei[i]));
        }
        if (rk[sa[i] + 1] > rk[sa[i + 1] + 1])
            add(f(sa[i]), f(sa[i + 1]));
    } 
    val[n + 1] = -1;
    for (int i = 1; i <= n; i++)
        val[i] = -2;
    int cur = 0;
    for (int i = 1; i <= n; i++) {
        int t = f(sa[i]);
        if (val[t] != -2) {
            assert(val[t] == cur);    
            continue;
        }
        for (ll x : v[t]) {
            assert(val[x] != -2);
            cur = max(cur, val[x] + 1);
        }
        assert(cur < 26);
        val[t] = cur;
    }
    for (int i = 1; i <= n; i++)
        cout << (char)('a' + val[f(i)]);
    cout << endl;
}
上一篇:详谈微信内推广H5和APP的分享链接如何做好防封防拦截处理


下一篇:Java语言基础(三)