两道题花式爆炸…怕是连省一都拿不到了QwQ
给定一个由AGCT组成的字符串,问长度为k的子串出现次数最多的出现了几次。
Sol:将字符串一一映射到 [0,4n] 的数上,用个桶统计一下答案。映射可以用哈希+自然溢出。
#include
using namespace std;
const int MAXN = 5000006;
unsigned long long bas = 4;
typedef unsigned long long ull;
int n, k;
char str[MAXN];
ull hash_val[MAXN], val = 1;
int st[1<<22|1];
int main()
{
scanf("%s", str+1);
n = strlen(str+1);
scanf("%d", &k);
for (register int i = 1; i <= k; i++) val *= bas;
for (register int i = 1; i <= n; i++) {
hash_val[i] = hash_val[i-1]*bas;
if (str[i] == 'C') hash_val[i]++;
if (str[i] == 'T') hash_val[i]+=2;
if (str[i] == 'G') hash_val[i]+=3;
}
int dat = 0;
for (register int i = 1; i+k-1 <= n; i++)
dat = max(dat, ++st[hash_val[i+k-1]-hash_val[i-1]*val]);
printf("%d\n", dat);
return 0;
}
给定集合 S 和若干个数列 an=kan−1+an−2 ,问 S 中使得 Sai,i∈S 取最大/最小的 ai 。
显然这个数列指数递减,向后试探若干项并计算贡献即可。
注意特判全为0!!!
#include
using namespace std;
const int MAXN = 100005;
int s[MAXN], n, m, k;
long long f[1005];
int main()
{
scanf("%d", &m);
for (int i = 1; i <= m; i++) scanf("%d", &s[i]);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld%d", &f[0], &f[1], &k);
if (f[0] == 0 && f[1] == 0) { printf("%d %d\n", s[1], s[1]); continue;}
int max_pos = -1, min_pos = -1;
int cur = 0, j = 1;
while (j <= m) {
while (j <= m && s[j] <= cur) {
if (min_pos == -1 || f[s[j]] < f[min_pos]) min_pos = s[j];
if (max_pos == -1 || f[s[j]] > f[max_pos]) {
max_pos = s[j];
}
j++;
}
if (cur && f[cur-1] > 0 && f[cur] > f[cur-1]) {
if (max_pos == -1) max_pos = s[m], min_pos = s[1];
else {
register int p = cur+1;
while (f[p] <= f[max_pos]) {
f[p+1] = k*f[p]+f[p-1];
p++;
}
if (s[m] >= p) max_pos = s[m];
if (j <= m && s[j] < p && f[s[j]] < f[min_pos]) min_pos = s[j];
}
break;
}
if (cur && f[cur-1] < 0 && f[cur] < f[cur-1]) {
if (max_pos == -1) max_pos = s[1], min_pos = s[m];
else {
register int p = cur+1;
while (f[p] >= f[min_pos]) {
f[p+1] = k*f[p]+f[p-1];
p++;
}
if (s[m] >= p) min_pos = s[m];
if (j <= m && s[j] < p && f[s[j]] > f[max_pos]) max_pos = s[j];
}
break;
}
f[cur+2] = k*f[cur+1]+f[cur];
cur++;
}
printf("%d %d\n", max_pos, min_pos);
}
return 0;
}
考虑dp。设 dis[i][j] 为从 i 开始到 j ,经过不超过 c[i] 条边,最长的距离。这个dp可以倍增的计算。
设 dp[i][j] 为 i 点钱为 j 最远的旅行,则:
询问可以二分答案做。
复杂度 O(n4+n3logC+Tlogp) 。
#include
using namespace std;
const int MAXN = 105;
int g[MAXN][MAXN][21], dis[2][MAXN][MAXN];
int dp[MAXN][MAXN*MAXN];
int n, m, C, T;
int p[MAXN], c[MAXN];
int main()
{
scanf("%d%d%d%d", &n, &m, &C, &T);
for (int i = 1; i <= n; i++) scanf("%d%d", &p[i], &c[i]), c[i] = min(c[i], C);
memset(g, -127/3, sizeof g);
for (int i = 1; i <= m; i++) {
int u, v, d; scanf("%d%d%d", &u, &v, &d);
g[u][v][0] = d;
}
for (int i = 1; i <= n; i++) g[i][i][0] = 0;
for (int k = 1; k <= 20; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
g[i][j][k] = g[i][j][k-1];
for (int t = 1; t <= n; t++)
g[i][j][k] = max(g[i][t][k-1]+g[t][j][k-1], g[i][j][k]);
// cerr << i << " " << j << " " << k << " " << g[i][j][k] << endl;
}
memset(dis, -127/3, sizeof dis);
for (int i = 1; i <= n; i++) {
int pre = 0, now = 1;
dis[pre][i][i] = dis[now][i][i] = 0;
for (int k = 0; k <= 20; k++)
if (c[i]&(1<for (int j = 1; j <= n; j++)
for (int t = 1; t <= n; t++) {
dis[now][i][j] = max(dis[now][i][j], dis[pre][i][t]+g[t][j][k]);
}
swap(now, pre);
memcpy(dis[now], dis[pre], sizeof dis[pre]);
}
if (pre == 1) memcpy(dis[0], dis[1], sizeof dis[1]);
}
memset(dp, 0, sizeof dp);
for (register int j = 1; j <= n*n; j++) {
for (register int i = 1; i <= n; i++) {
if (p[i] <= j)
for (register int k = 1; k <= n; k++)
dp[i][j] = max(dp[i][j], dp[k][j-p[i]]+dis[0][i][k]);
}
}
for (int i = 1; i <= T; i++) {
int s, q, d;
scanf("%d%d%d", &s, &q, &d);
if (dp[s][q] < d) puts("-1");
else {
int L = 1, R = q, mid;
while (L <= R) {
mid = (L+R)>>1;
if (dp[s][mid] < d) L = mid+1;
else R = mid-1;
}
printf("%d\n", q-L);
}
}
return 0;
}
给定 n≤2×106 ,要求构造一个无向图,使得三元环个数为 n , |V|≤500 。
考虑先做一个尽可能大的团,然后贪心给新节点与团中的节点连尽可能多的边。
#include
using namespace std;
const int MAXN = 505;
int n, val = 3, x = 0;
int g[MAXN][MAXN];
int main()
{
scanf("%d", &n);
while (val*(val-1)*(val-2)/6 <= n)
val++;
val--;
n -= val*(val-1)*(val-2)/6;
x = val;
for (int i = 1; i <= val; i++)
for (int j = 1; j < i; j++)
g[i][j] = g[j][i] = 1;
for (int i = val; i >= 2; i--) {
if (n >= i*(i-1)/2) {
n -= i*(i-1)/2;
++x;
for (int j = 1; j <= i; j++) g[j][x] = g[x][j] = 1;
}
}
while (n) {
++x;
g[1][x] = g[x][1] = g[2][x] = g[x][2] = 1;
n--;
}
printf("%d\n", x);
for (int i = 1; i < x; i++) {
for (int j = i+1; j <= x; j++)
printf("%d ", g[i][j]);
puts("");
}
return 0;
}
题意较长orz.
如果没有修改,则很容易用一个并查集维护mex。如果有修改,则并查集维护的不再是答案,而是答案的上限。考虑修改的影响,扔出一个 p[i] 会使得答案 ≤p[i] 。同时多次扔出是具有单调性的(这次扔出的一定在上一次扔出的捡回后捡回,所以这次扔出的比上次小,则上次就没有贡献了)。可以用单调队列维护之。
#include
using namespace std;
namespace IO{
int c;
unsigned int seed;
unsigned int randnum(){
seed^=seed<<13;
seed^=seed>>17;
seed^=seed<<5;
return seed;
}
inline int read(int &x){scanf("%d",&x);return x;}
inline void init_case(int &m,int &a,int &b,int &d,int p[]){
scanf("%d%u%d%d%d%d",&m,&seed,&a,&b,&c,&d);
for(int i=1;i<=m;i++){
if(randnum()%c==0)p[i]=-1;
else p[i]=randnum()%b;
}
}
inline void update_ans(unsigned int &ans_sum,unsigned int cur_ans,int no){
const static unsigned int mod=998244353;
ans_sum^=(long long)no*(no+7)%mod*cur_ans%mod;
}
}
using IO::read;
using IO::init_case;
using IO::update_ans;
const int MAXN = 2000005;
int m, a, b, d, p[MAXN];
int T;
bitset bt, inside;
unsigned int ans_sum = 0, cur_ans = 0;
int max_ans = 0;
int que[MAXN];
int l = 1, r = 0;
int q[MAXN], ql = 1, qr = 0; // 普通队列
int fa[MAXN];
inline int findf(int nd)
{ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }
int tot = 0;
int main()
{
read(T);
while (T--) {
init_case(m, a, b, d, p);
bt.reset(), inside.reset();
memset(fa, 0, sizeof fa);
cur_ans = ans_sum = 0;
l = 1, r = 0, ql = 1, qr = 0;
for (register int i = 0; i <= a; i++) bt[i] = inside[i] = 1, fa[i] = a+1;
for (register int i = 1; i <= m; i++) {
if (d == 1) {
if (p[i] != -1 && !bt[p[i]]) {
bt[p[i]] = inside[p[i]] = 1;
fa[p[i]] = findf(p[i]+1);
cur_ans = findf(0);
update_ans(ans_sum, cur_ans, i);
}
} else {
if (p[i] != -1 && !bt[p[i]]) {
bt[p[i]] = inside[p[i]] = 1;
fa[p[i]] = findf(p[i]+1);
} else if (p[i] != -1 && inside[p[i]]) {
while (l <= r && p[i] < que[r]) r--;
que[++r] = p[i];
q[++qr] = p[i];
inside[p[i]] = 0;
} else if (ql <= qr) {
if (q[ql] == que[l]) l++;
inside[q[ql++]] = 1;
} else {continue;}
cur_ans = findf(0);
if (l <= r) cur_ans = min(cur_ans, (unsigned int)que[l]);
update_ans(ans_sum, cur_ans, i);
}
}
printf("%u\n", ans_sum);
}
return 0;
}