咕了整整一天才来写 2333
这次邓老师没来 /kk,不过打的还算不错,可惜还是有一题没调出来,不然就是 11 题队了 /ll
1001
题意好像有点奇怪,不过 djq 还是一眼看对了,拿了这题一血。
题解
看对题就没啥好说的了,裸签到题,把点按权值从大到小排序,不断加入点,用并查集维护当前连通块个数,乘一下相邻两个权值的差加起来即可。复杂度 。
code by djq
#include <bits/stdc++.h>
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MOD = 998244353;
int n, m, b[100005];
PII ver[100005];
int rt[100005];
int root(int x)
{
if(rt[x] == x) return x;
return rt[x] = root(rt[x]);
}
bool unite(int u, int v)
{
u = root(u); v = root(v);
if(u == v) return false;
rt[v] = u;
return true;
}
vector<int> G[100005];
void solve()
{
scanf("%d%d", &n, &m);
rep1(i, n) scanf("%d", &b[i]);
rep1(i, n) ver[i] = MP(b[i], i);
sort(ver + 1, ver + 1 + n);
reverse(ver + 1, ver + 1 + n);
ver[n + 1].first = 0;
rep1(i, n) G[i].clear();
rep(i, m) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
rep1(i, n) rt[i] = i;
int cnt = 0;
LL ans = 0;
rep1(i, n) {
cnt ++;
int v = ver[i].second;
rep(j, G[v].size()) {
int u = G[v][j];
if(b[u] >= b[v] && unite(u, v)) cnt --;
}
ans += 1LL * (ver[i].first - ver[i + 1].first) * cnt;
}
printf("%lld\n", ans);
}
int main()
{
int T;
scanf("%d", &T);
while(T --) solve();
return 0;
}
1002
其实应该也算个比较套路的题?
考虑拆掉绝对值之后每个点的贡献是 -2,0,2,如果在首尾的话比较特别,是 -1 或 1.
由于 互不相同,大概按照十三练里波浪那题 dp 就差不多了。
表示当前从小到大加到了第 个数,有 个连续段,其中首尾放了 0/1/2 个数时的第 大。
转移的话就枚举当前的几种情况:产生新的段;在原来段的开头或结尾加入一个(即不改变段数);合并两段。以及还要考虑当前数字是否放在开头/结尾。
合并前 大,大概写个类似归并的东西就可以了。复杂度 。
(转移还是稍微有些复杂的)
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN = 605, MOD = 1e9 + 7;
struct Data { int v, c; } f[2][MAXN][3][25];
int T, n, K, aa[MAXN], len[2][MAXN][3];
void calc(Data *a, const Data *b, int &na, int nb, int val, LL coef) {
if (nb == 0) return;
if (na == 0) {
na = nb;
for (int i = 1; i <= na; i++)
a[i] = Data { b[i].v + val, b[i].c * coef % MOD };
return;
}
static Data d[25];
int i = 1, j = 1, c = 0;
while (c < K && (i <= na || j <= nb)) {
if (j > nb || (i <= na && a[i].v > b[j].v + val))
d[++c] = a[i], ++i;
else if (i > na || (j <= nb && b[j].v + val > a[i].v))
d[++c] = Data { b[j].v + val, b[j].c * coef % MOD }, ++j;
else d[++c] = Data { a[i].v, (a[i].c + b[j].c * coef) % MOD }, ++i, ++j;
}
for (int i = 1; i <= c; i++) {
a[i] = d[i];
if (i < c && d[i].v == d[i + 1].v) {
puts("fuck");
}
}
na = c;
}
int main() {
for (scanf("%d", &T); T--;) {
scanf("%d%d", &n, &K);
for (int i = 1; i <= n; i++)
scanf("%d", aa + i);
sort(aa + 1, aa + 1 + n);
memset(len, 0, sizeof(len));
f[0][0][0][1] = Data { 0, 1 };
len[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
int a = i & 1, b = !a;
memset(len[a], 0, sizeof(len[a]));
for (int j = 1; j <= i; j++) {
calc(f[a][j][0], f[b][j - 1][0], len[a][j][0], len[b][j - 1][0], -2 * aa[i], j);
calc(f[a][j][0], f[b][j][0], len[a][j][0], len[b][j][0], 0, 2 * j);
calc(f[a][j][0], f[b][j + 1][0], len[a][j][0], len[b][j + 1][0], 2 * aa[i], j);
calc(f[a][j][1], f[b][j - 1][1], len[a][j][1], len[b][j - 1][1], -2 * aa[i], j - 1);
calc(f[a][j][1], f[b][j][1], len[a][j][1], len[b][j][1], 0, 2 * j - 1);
calc(f[a][j][1], f[b][j + 1][1], len[a][j][1], len[b][j + 1][1], 2 * aa[i], j);
calc(f[a][j][1], f[b][j - 1][0], len[a][j][1], len[b][j - 1][0], -aa[i], 2);
calc(f[a][j][1], f[b][j][0], len[a][j][1], len[b][j][0], aa[i], 2);
calc(f[a][j][2], f[b][j - 1][2], len[a][j][2], len[b][j - 1][2], -2 * aa[i], j - 2);
calc(f[a][j][2], f[b][j][2], len[a][j][2], len[b][j][2], 0, 2 * j - 2);
calc(f[a][j][2], f[b][j + 1][2], len[a][j][2], len[b][j + 1][2], 2 * aa[i], j);
calc(f[a][j][2], f[b][j - 1][1], len[a][j][2], len[b][j - 1][1], -aa[i], 1);
calc(f[a][j][2], f[b][j][1], len[a][j][2], len[b][j][1], aa[i], 1);
}
}
for (int i = 1; i <= K; i++) {
const Data &d = f[n & 1][1][2][i];
if (d.c) printf("%d %d\n", d.v, d.c);
else puts("-1");
}
}
return 0;
}
1003
这场比赛里最神的一道题了吧,好像很多人都觉得 能过然而被卡了 2333.
神仙随机化题,考虑随机 个 中的实数,最小值的期望是 。于是考虑给每种颜色随机一个权值,然后求链上权值最小值就可以近似的得到颜色个数。我们多随机几次,取这些最小值的平均值进行比较即可。
于是可以直接树剖维护(倍增空间大预处理复杂度大过不了 qwq),也可以全局平衡二叉树维护(不过好像这题不是查询到根的链,可能难写一些)。
懒得写了,直接贴个题解代码吧。
#include<cstdio>
#include<algorithm>
#pragma GCC optimize(2)
using namespace std;
typedef unsigned int U;
typedef long long ll;
const int N=500010,K=30;
const U inf=~0U;
int Case,n,m,i,j,last,op,x,y,A,B,C,D,col[N],g[N],v[N<<1],nxt[N<<1],ed;
int f[N],d[N],size[N],son[N],top[N],loc[N],q[N],dfn;
U w[N][K],val[1111111][K],tmp[K];
U SX=335634763,SY=873658265,SZ=192849106,SW=746126501;
inline U xorshift128(){
U t=SX^(SX<<11);
SX=SY;
SY=SZ;
SZ=SW;
return SW=SW^(SW>>19)^t^(t>>8);
}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs1(int x,int y){
f[x]=y,d[x]=d[y]+1,size[x]=1;
for(int i=g[x];i;i=nxt[i]){
int u=v[i];
if(u==y)continue;
dfs1(u,x);
size[x]+=size[u];
if(size[u]>size[son[x]])son[x]=u;
}
}
void dfs2(int x,int y){
q[loc[x]=++dfn]=x;
top[x]=y;
if(son[x])dfs2(son[x],y);
for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]&&v[i]!=son[x])dfs2(v[i],v[i]);
}
inline void up(int x){for(int i=0;i<K;i++)val[x][i]=min(val[x<<1][i],val[x<<1|1][i]);}
void build(int x,int a,int b){
if(a==b){
int o=col[q[a]];
for(int i=0;i<K;i++)val[x][i]=w[o][i];
return;
}
int mid=(a+b)>>1;
build(x<<1,a,mid),build(x<<1|1,mid+1,b);
up(x);
}
void change(int x,int a,int b,int c,int p){
if(a==b){
for(int i=0;i<K;i++)val[x][i]=w[p][i];
return;
}
int mid=(a+b)>>1;
if(c<=mid)change(x<<1,a,mid,c,p);else change(x<<1|1,mid+1,b,c,p);
up(x);
}
inline void umin(U&a,U b){a>b?(a=b):0;}
void ask(int x,int a,int b,int c,int d){
if(c<=a&&b<=d){
for(int i=0;i<K;i++)umin(tmp[i],val[x][i]);
return;
}
int mid=(a+b)>>1;
if(c<=mid)ask(x<<1,a,mid,c,d);
if(d>mid)ask(x<<1|1,mid+1,b,c,d);
}
inline ll estimate(int x,int y){
for(int i=0;i<K;i++)tmp[i]=inf;
for(;top[x]!=top[y];x=f[top[x]]){
if(d[top[x]]<d[top[y]])swap(x,y);
ask(1,1,n,loc[top[x]],loc[x]);
}
if(d[x]<d[y])swap(x,y);
ask(1,1,n,loc[y],loc[x]);
ll ret=0;
for(int i=0;i<K;i++)ret+=tmp[i];
return ret;
}
int main(){
for(i=1;i<N;i++)for(j=0;j<K;j++)w[i][j]=xorshift128();
scanf("%d",&Case);
while(Case--){
scanf("%d%d",&n,&m);
for(ed=dfn=last=i=0;i<=n;i++)g[i]=f[i]=d[i]=size[i]=son[i]=0;
for(i=1;i<=n;i++)scanf("%d",&col[i]);
for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
while(m--){
scanf("%d%d%d",&op,&A,&B);
A^=last,B^=last;
if(op==1)change(1,1,n,loc[A],B);
else{
scanf("%d%d",&C,&D);
C^=last,D^=last;
ll E=estimate(A,B),F=estimate(C,D);
if(E<F){
puts("Yes");
last++;
}else puts("No");
}
}
}
}
1004
本来以为 能过的,后来为了求稳还是写了发主席树……
考虑当前限制了 这个矩形,那么最终的路径要么经过 这个格子或其上面的某个鸽子,要么经过 或其左边的某个格子。
注意到权值定义为 ,也就是说能选大的就尽量选大的。我们考虑 dp, 表示 到 能得到的最大值, 表示 到 的最大值,于是现在的问题就落到了比较上。
我们用可持久化线段树维护每种数字的出现次数,比较大小的话就直接线段树二分,每个节点维护下面次数的 hash 值和答案(刚好答案也可以作为第二种 hash)。
接下来要算一下对于 ,强制经过其上方的格子能获得的最大值。首先要么是 的答案,要么是强制经过 的答案。这个时候我们发现需要比较两个主席树加起来,和另外两个主席树加起来的大小(因为要把 作为 的权值)。hash 只要是正常的字符串 hash,就可以直接加,因此还是可以线段树二分的。复杂度 。
自己写的代码找不到了,下面是 code by djq。
#include <bits/stdc++.h>
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int D0 = 243, D1;
int MOD0 = 998244353, MOD1 = 1e9 + 7;
int PW0[200005], PW1[200005];
void init_pw()
{
PW0[0] = PW1[0] = 1;
rep1(i, 200000) {
PW0[i] = 1LL * PW0[i - 1] * D0 % MOD0;
PW1[i] = 1LL * PW1[i - 1] * D1 % MOD1;
}
}
namespace segt
{
int cnt[8000005], hs0[8000005], hs1[8000005], lson[8000005], rson[8000005], tot;
void init()
{
memset(hs0, 0, sizeof(hs0));
memset(hs1, 0, sizeof(hs1));
memset(lson, -1, sizeof(lson));
memset(rson, -1, sizeof(rson));
tot = 0;
}
void pushup(int cur)
{
hs0[cur] = ((lson[cur] == -1 ? 0 : hs0[lson[cur]]) + (rson[cur] == -1 ? 0 : hs0[rson[cur]])) % MOD0;
hs1[cur] = ((lson[cur] == -1 ? 0 : hs1[lson[cur]]) + (rson[cur] == -1 ? 0 : hs1[rson[cur]])) % MOD1;
}
int modify(int cur, int dat, int cl, int cr)
{
int ret = tot ++;
if(cl == cr) {
cnt[ret] = (cur == -1 ? 0 : cnt[cur]) + 1;
hs0[ret] = 1LL * PW0[dat] * cnt[ret] % MOD0;
hs1[ret] = 1LL * PW1[dat] * cnt[ret] % MOD1;
return ret;
}
int mid = cl + cr >> 1;
if(dat <= mid) {
lson[ret] = modify(cur == -1 ? -1 : lson[cur], dat, cl, mid);
rson[ret] = cur == -1 ? -1 : rson[cur];
} else {
lson[ret] = cur == -1 ? -1 : lson[cur];
rson[ret] = modify(cur == -1 ? -1 : rson[cur], dat, mid + 1, cr);
}
pushup(ret);
return ret;
}
int comp(int a, int b, int cl, int cr)
{
if(a == -1 && b == -1) return 0;
if(a == -1) return 1;
if(b == -1) return -1;
if(hs0[a] == hs0[b] && hs1[a] == hs1[b]) return 0;
if(cl == cr) return cnt[a] < cnt[b] ? 1 : -1;
int mid = cl + cr >> 1;
int ret = comp(rson[a], rson[b], mid + 1, cr);
if(ret == 0) ret = comp(lson[a], lson[b], cl, mid);
return ret;
}
int comp2(int a0, int a1, int b0, int b1, int cl, int cr)
{
int ah0 = ((a0 == -1 ? 0 : hs0[a0]) + (a1 == -1 ? 0 : hs0[a1])) % MOD0;
int ah1 = ((a0 == -1 ? 0 : hs1[a0]) + (a1 == -1 ? 0 : hs1[a1])) % MOD1;
int bh0 = ((b0 == -1 ? 0 : hs0[b0]) + (b1 == -1 ? 0 : hs0[b1])) % MOD0;
int bh1 = ((b0 == -1 ? 0 : hs1[b0]) + (b1 == -1 ? 0 : hs1[b1])) % MOD1;
if(ah0 == bh0 && ah1 == bh1) return 0;
if(cl == cr) return (a0 == -1 ? 0 : cnt[a0]) + (a1 == -1 ? 0 : cnt[a1]) <
(b0 == -1 ? 0 : cnt[b0]) + (b1 == -1 ? 0 : cnt[b1]) ? 1 : -1;
int mid = cl + cr >> 1;
int ret = comp2(a0 == -1 ? -1 : rson[a0], a1 == -1 ? -1 : rson[a1],
b0 == -1 ? -1 : rson[b0], b1 == -1 ? -1 : rson[b1], mid + 1, cr);
if(ret == 0) ret = comp2(a0 == -1 ? -1 : lson[a0], a1 == -1 ? -1 : lson[a1],
b0 == -1 ? -1 : lson[b0], b1 == -1 ? -1 : lson[b1], cl, mid);
return ret;
}
}
int n, q, a[405][405];
int dp0[405][405], f1[405][405], dp1[405][405], lpos[405][405], upos[405][405];
void solve()
{
scanf("%d%d", &n, &q);
rep1(i, n) rep1(j, n) scanf("%d", &a[i][j]);
D1 = n * n;
init_pw();
segt::init();
memset(dp0, -1, sizeof(dp0));
memset(f1, -1, sizeof(f1));
memset(dp1, -1, sizeof(dp1));
memset(lpos, 0, sizeof(lpos));
memset(upos, 0, sizeof(upos));
rep1(i, n) rep1(j, n) {
dp0[i][j] = segt::comp(dp0[i - 1][j], dp0[i][j - 1], 0, 262143) == -1 ? dp0[i - 1][j] : dp0[i][j - 1];
dp0[i][j] = segt::modify(dp0[i][j], a[i][j], 0, 262143);
}
for(int i = n; i >= 1; i --) for(int j = n; j >= 1; j --) {
f1[i][j] = segt::comp(dp1[i + 1][j], dp1[i][j + 1], 0, 262143) == -1 ? dp1[i + 1][j] : dp1[i][j + 1];
dp1[i][j] = segt::modify(f1[i][j], a[i][j], 0, 262143);
}
rep1(i, n) rep1(j, n) {
int lj = lpos[i][j - 1];
lpos[i][j] = segt::comp2(dp0[i][lj], f1[i][lj], dp0[i][j], f1[i][j], 0, 262143) == -1 ? lj : j;
}
rep1(i, n) rep1(j, n) {
int ui = upos[i - 1][j];
upos[i][j] = segt::comp2(dp0[ui][j], f1[ui][j], dp0[i][j], f1[i][j], 0, 262143) == -1 ? ui : i;
}
rep(i, q) {
int lx, rx, ly, ry;
scanf("%d%d%d%d", &lx, &rx, &ly, &ry);
int i0 = rx + 1, j0 = ly - 1, i1 = lx - 1, j1 = ry + 1;
j0 = lpos[i0][j0];
i1 = upos[i1][j1];
int ret = segt::comp2(dp0[i0][j0], f1[i0][j0], dp0[i1][j1], f1[i1][j1], 0, 262143) == -1 ?
(segt::hs1[dp0[i0][j0]] + segt::hs1[f1[i0][j0]]) % MOD1 : (segt::hs1[dp0[i1][j1]] + segt::hs1[f1[i1][j1]]) % MOD1;
printf("%d\n", ret);
}
}
int main()
{
int T;
scanf("%d", &T);
while(T --) solve();
return 0;
}
1005
这个题还是比较 easy 的,考虑对于一个工人,找到他最小值的所在位置扔到一个 vector 里(为了不出错把最小值左右两个整数都加入)。
接下来考虑对于 vector 里的每个元素,找到第一个没有在 set 里出现过的大于它的数字 ,把 扔到 set 里。
同理的,对于 vector 里的每个元素,找到第一个没有在 set 里出现过的小于它的数字 ,把 扔到 set 里。
然后对于 set 里的元素,和工人两两连边,跑 遍费用流即可。不难发现最优解肯定能够被包含在上面的构造方法中,因此点数 ,边数 ,肯定能过。
code by djq
#include <bits/stdc++.h>
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const LL INF = 1.3e17;
namespace flow
{
const int MAXN = 400;
struct edge
{
int to, cap;
LL cost;
};
int n, m;
edge ed[30005];
vector<int> G[405];
void clear()
{
m = 0;
rep(i, n) G[i].clear();
}
void add_edge(int u, int v, int fl, LL co)
{
ed[m].to = v; ed[m].cap = fl; ed[m].cost = co; G[u].push_back(m); m ++;
ed[m].to = u; ed[m].cap = 0; ed[m].cost = -co; G[v].push_back(m); m ++;
}
LL h[405];
LL pat[405];
int prev[405];
bool vis[405];
bool dijk(int s, int t)
{
rep(i, n) {
pat[i] = INF;
vis[i] = false;
}
pat[s] = 0;
priority_queue<pair<LL, int> > que;
que.push(MP(0, s));
while(!que.empty()) {
int cur = que.top().second;
que.pop();
if(vis[cur]) continue;
vis[cur] = true;
rep(i, G[cur].size()) {
int ce = G[cur][i];
if(ed[ce].cap == 0) continue;
if(pat[ed[ce].to] > pat[cur] + ed[ce].cost) {
pat[ed[ce].to] = pat[cur] + ed[ce].cost;
prev[ed[ce].to] = ce;
que.push(MP(-pat[ed[ce].to], ed[ce].to));
}
}
}
return pat[t] != INF;
}
void mcf(int s, int t, LL* ret)
{
rep(i, n) h[i] = 0;
LL ans = 0;
while(dijk(s, t)) {
//in this occasion flow must be 1
int cur = t, cf = 1;
while(cur != s) {
ed[prev[cur]].cap -= cf;
ed[prev[cur] ^ 1].cap += cf;
cur = ed[prev[cur] ^ 1].to;
}
rep(i, m) ed[i].cost += pat[ed[i ^ 1].to] - pat[ed[i].to];
rep(i, n) h[i] += pat[i];
ans += h[t] * cf;
*(++ ret) = ans;
}
}
}
int n, m;
LL a[55], b[55], c[55];
set<LL> S;
vector<LL> hv, nhv;
LL ans[55];
void solve()
{
scanf("%d%d", &n, &m);
rep(i, n) scanf("%lld%lld%lld", &a[i], &b[i], &c[i]);
hv.clear();
nhv.clear();
rep(i, n) {
if(b[i] > 0) hv.push_back(1);
else {
LL pt = max(min((-b[i]) / 2 / a[i], m - 1LL), 1LL);
if(pt >= 1 && pt <= m) hv.push_back(pt);
if(pt + 1 >= 1 && pt + 1 <= m) hv.push_back(pt + 1);
}
}
S.clear();
rep(i, hv.size()) {
int cur = hv[i];
while(S.find(cur) != S.end()) cur ++;
if(cur < 1 || cur > m) continue;
nhv.push_back(cur);
S.insert(cur);
}
S.clear();
rep(i, hv.size()) {
int cur = hv[i];
while(S.find(cur) != S.end()) cur --;
if(cur < 1 || cur > m) continue;
nhv.push_back(cur);
S.insert(cur);
}
sort(nhv.begin(), nhv.end());
nhv.resize(unique(nhv.begin(), nhv.end()) - nhv.begin());
flow::n = n + nhv.size() + 2;
flow::clear();
rep(i, n) flow::add_edge(flow::n - 2, i, 1, 0);
rep(i, nhv.size()) flow::add_edge(i + n, flow::n - 1, 1, 0);
rep(i, n) rep(j, nhv.size())
flow::add_edge(i, j + n, 1, a[i] * nhv[j] * nhv[j] + b[i] * nhv[j] + c[i]);
flow::mcf(flow::n - 2, flow::n - 1, ans);
rep1(i, n) printf("%lld%c", ans[i], " \n"[i == n]);
}
int main()
{
int T;
scanf("%d", &T);
while(T --) solve();
return 0;
}
1006
刚开始以为是那个 PA 原题,吓懵了……仔细一看发现原来只需要判断就行。那就是签到题了,随便取几个质数膜一下判相不相等即可。复杂度 。
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
const int MAXN = 2000005, B = 5;
const int MOD[B] = { 1000000007, 1000000009, 1000000021, 1000000033, 1000000087 };
int fib[MAXN][5], arr[B], brr[B], crr[B], T, na, nb, nc;
bool cc[MAXN];
void init() {
int n = 2e6 + 3;
for (int i = 0; i < B; i++)
fib[0][i] = 1, fib[1][i] = 1;
for (int i = 2; i <= n; i++)
for (int j = 0; j < B; j++)
fib[i][j] = (fib[i - 1][j] + fib[i - 2][j]) % MOD[j];
}
int main() {
init();
for (scanf("%d", &T); T--;) {
scanf("%d", &na);
memset(arr, 0, sizeof(arr));
memset(brr, 0, sizeof(brr));
memset(crr, 0, sizeof(crr));
for (int i = 1; i <= na; i++) {
int t; scanf("%d", &t);
if (t) for (int j = 0; j < B; j++)
arr[j] = (arr[j] + fib[i][j]) % MOD[j];
}
scanf("%d", &nb);
for (int i = 1; i <= nb; i++) {
int t; scanf("%d", &t);
if (t) for (int j = 0; j < B; j++)
brr[j] = (brr[j] + fib[i][j]) % MOD[j];
}
scanf("%d", &nc);
for (int i = 1; i <= nc; i++) {
int t; scanf("%d", &t);
cc[i] = t;
if (t) for (int j = 0; j < B; j++)
crr[j] = (crr[j] + fib[i][j]) % MOD[j];
}
for (int i = 0; i < B; i++)
arr[i] = (LL)arr[i] * brr[i] % MOD[i];
for (int i = 1; i <= nc; i++) if (!cc[i]) {
if (cc[i - 1] || cc[i + 1]) continue;
bool flag = true;
for (int j = 0; j < B; j++)
if ((crr[j] + fib[i][j]) % MOD[j] != arr[j])
{ flag = false; break; }
if (flag) {
printf("%d\n", i);
break;
}
}
}
return 0;
}
1007
要求直径最小,可以二分答案,然后记 表示 的子树,有 条边选了权值 时 子树的深度最小是多少。转移的话枚举当前儿子选了多少个 ,如果当前儿子和之前的儿子形成的路径已经 了就不能转移。最后看有没有合法方案即可。复杂度 。
code by lqs
#include<bits/stdc++.h>
using namespace std;
int test,n,k,x,y,a,b;
long long l,r,mid,dp[22222][22],f[22],gg[22],nf[22];
struct edge
{
int to,a,b;
};
vector<edge> g[22222];
void dfs(int i,int fa)
{
for (int j=0;j<g[i].size();j++)
{
int to=g[i][j].to;
if (to==fa) continue;
dfs(to,i);
}
for (int j=0;j<=k;j++) f[j]=1e18;
f[0]=0;
for (int j=0;j<g[i].size();j++)
{
int to=g[i][j].to;
if (to==fa) continue;
gg[0]=dp[to][0]+g[i][j].b;
for (int h=1;h<=k;h++)
{
gg[h]=min(dp[to][h]+g[i][j].b,dp[to][h-1]+g[i][j].a);
}
for (int h=0;h<=k;h++) nf[h]=1e18;
for (int h=0;h<=k;h++)
{
for (int p=0;p+h<=k;p++)
{
if (f[h]+gg[p]<=mid) nf[h+p]=min(nf[h+p],max(f[h],gg[p]));
}
}
for (int h=0;h<=k;h++) f[h]=nf[h];
}
for (int j=0;j<=k;j++) dp[i][j]=f[j];
}
bool check()
{
dfs(1,0);
return (dp[1][k]<=mid);
}
int main()
{
scanf("%d",&test);
while(test--)
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) g[i].clear();
for (int i=1;i<n;i++)
{
scanf("%d%d%d%d",&x,&y,&a,&b);
g[x].push_back((edge){y,a,b});
g[y].push_back((edge){x,a,b});
}
l=0;r=1e14;
while(l<=r)
{
mid=(l+r)>>1;
if (check()) r=mid-1;
else l=mid+1;
}
printf("%lld\n",l);
}
return 0;
}
1008
假设没有加入删除操作,我们考虑把这些函数按照 排序,接下来从左向右扫维护一个栈,假设当前扫到了 ,如果 和 的交点在 和 的交点的左边,那么 就可以被删掉了。
查询的话直接二分在哪个函数上即可。
现在有了插入删除操作,我们直接线段树分治。对于每个线段树节点都像上面那样处理;对于一个询问,查询它在线段树上所有祖先的答案取最小值即可。
复杂度 。
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
const int MAXN = 300005;
struct Func {
int a; LL b;
LL calc(int x) const {
LL t = x - a;
return t * t * t * t + b;
}
} fun[MAXN];
int inter(const Func &a, const Func &b) {
int l = 0, r = 5e4 + 5;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (a.calc(mid) < b.calc(mid)) l = mid;
else r = mid;
}
return l;
}
int tn, T, n, m, beg[MAXN], ed[MAXN], id[MAXN];
struct Seg { Func f; int x; };
vector<Seg> tr[MAXN];
struct Qry { int t, p; };
vector<Qry> qry;
void update(int a, int b, int d, int l = 1, int r = tn, int k = 1) {
if (a > r || b < l) return;
if (a <= l && b >= r) {
while (!tr[k].empty() && (tr[k].back().f.a == fun[d].a ||
tr[k].back().x >= inter(tr[k].back().f, fun[d])))
tr[k].pop_back();
if (tr[k].empty()) tr[k].push_back(Seg { fun[d], 0 });
else tr[k].push_back(Seg { fun[d], inter(tr[k].back().f, fun[d]) });
return;
}
int mid = (l + r) >> 1;
update(a, b, d, l, mid, k << 1);
update(a, b, d, mid + 1, r, k << 1 | 1);
}
LL ask(int k, int x) {
LL ans = 9e18;
for (k += tn - 1; k > 0; k >>= 1) {
int l = 0, r = tr[k].size();
if (!r) continue;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (tr[k][mid].x < x) l = mid;
else r = mid;
}
chkmin(ans, tr[k][l].f.calc(x));
}
return ans;
}
int main() {
for (scanf("%d", &T); T--;) {
scanf("%d%d", &n, &m);
int tim = 1;
for (int i = 1; i <= n; i++)
scanf("%d%lld", &fun[i].a, &fun[i].b);
for (int i = 1; i <= m; i++) {
int o, a; LL b; scanf("%d%d", &o, &a);
if (o == 1) {
scanf("%lld", &b);
fun[++n] = Func { a, b };
beg[n] = ++tim;
} else if (o == 2) {
ed[a] = tim++;
} else qry.push_back(Qry { tim, a });
}
for (int i = 1; i <= n; i++) id[i] = i;
sort(id + 1, id + 1 + n, [&](int x, int y)
{ return fun[x].a == fun[y].a ? fun[x].b > fun[y].b : fun[x].a < fun[y].a; });
for (tn = 1; tn < tim; tn <<= 1);
for (int i = 1; i <= n; i++) {
int d = id[i];
if (ed[d] == 0) ed[d] = tim;
update(beg[d], ed[d], d);
}
for (const Qry &q : qry) {
LL t = ask(q.t, q.p);
if (t > 8e18) puts("-1");
else printf("%lld\n", t);
}
qry.clear();
for (int i = 1; i <= tn * 2; i++)
tr[i].clear();
for (int i = 1; i <= n; i++)
beg[i] = ed[i] = 0;
}
return 0;
}
1009
暴力可过题。考虑每种轮廓的外接矩形的面积和不超过 ,直接暴力枚举外接矩形内的所有点,判一下在不在轮廓内即可。判断的话可以看他左边的轮廓线条数是不是奇数,是奇数就在里面,否则就在外面。
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
namespace IO {
const int MAXR = 10000000;
char _READ_[MAXR], _PRINT_[MAXR];
int _READ_POS_, _PRINT_POS_, _READ_LEN_;
inline char readc() {
#ifndef ONLINE_JUDGE
return getchar();
#endif
if (!_READ_POS_) {
if (feof(stdin)) return -1;
_READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
}
char c = _READ_[_READ_POS_++];
if (_READ_POS_ == _READ_LEN_) _READ_POS_ = 0;
return c;
}
template<typename T> inline int read(T &x) {
x = 0; register int flag = 1, c;
while (((c = readc()) < '0' || c > '9') && c != '-')
if (c < 0) return -1;
if (c == '-') flag = -1; else x = c - '0';
while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
x *= flag; return 0;
}
inline int read(char *s) {
register int len = 0, c;
while (isspace(c = readc()) || c <= 0)
if (c < 0) return -1;
s[len++] = c;
while (!isspace(c = readc()) && c) s[len++] = c;
s[len] = 0;
return len;
}
template<typename T1, typename ...T2> inline int read(T1 &a, T2&... x) {
return read(a) | read(x...);
}
inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
inline void printc(char c) {
if (!c) return;
_PRINT_[_PRINT_POS_++] = c;
if (_PRINT_POS_ == MAXR) ioflush();
}
template<typename T> inline void print(T x, char c = '\n') {
if (x < 0) printc('-'), x = -x;
if (x) {
static char sta[20];
register int tp = 0;
for (; x; x /= 10) sta[tp++] = x % 10 + '0';
while (tp > 0) printc(sta[--tp]);
} else printc('0');
printc(c);
}
inline void print(char *s, char c = '\n') {
for (int i = 0; s[i]; i++) printc(s[i]);
printc(c);
}
inline void print(const char *s, char c = '\n') {
for (int i = 0; s[i]; i++) printc(s[i]);
printc(c);
}
template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
print(x, ' '), print(y...);
}
}
const int MAXN = 405, MAXM = 400005;
int mt[MAXN][MAXN], vis[MAXM], n, m, Q, T, tim;
int tag[MAXN][MAXN];
char str[MAXM];
int main() {
for (IO::read(T); T--;) {
IO::read(n, m, Q);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
IO::read(mt[i][j]);
while (Q--) {
int x, y; IO::read(x, y);
int l = IO::read(str), x1 = n, y1 = m, x2 = 0, y2 = m;
for (int i = 0; i < l; i++) {
chkmin(x1, x), chkmin(y1, y);
chkmax(x2, x), chkmax(y2, y);
switch (str[i]) {
case 'L': tag[x--][y + 1] ^= 1; break;
case 'R': tag[++x][y + 1] ^= 1; break;
case 'D': --y; break;
case 'U': ++y; break;
}
}
++x1, ++y1;
++tim;
int ans = 0;
for (int i = x1; i <= x2; i++) {
int t = 0;
for (int j = y1; j <= y2; j++) {
t ^= tag[i][j];
if (t && vis[mt[i][j]] != tim)
vis[mt[i][j]] = tim, ++ans;
tag[i][j] = 0;
}
tag[i][y2 + 1] = 0;
}
IO::print(ans);
}
}
IO::ioflush();
return 0;
}
1010
暴搜题,每种物品至少会选择一个,因此复杂度最坏为 。
#include<bits/stdc++.h>
using namespace std;
int test,n,k,a[55],b[55],c[55],d[55];
int cnt[55],id[55][55],sa,sb,sc,sd,t,mx;
long long ans;
void dfs(int i)
{
if (i==mx+1)
{
ans=max(ans,1ll*(100+sa)*(100+sb)*(100+sc)*(100+sd));
return;
}
if (!cnt[i])
{
dfs(i+1);
return;
}
for (int j=1;j<=cnt[i];j++)
{
sa+=a[id[i][j]];sb+=b[id[i][j]];sc+=c[id[i][j]];sd+=d[id[i][j]];
dfs(i+1);
sa-=a[id[i][j]];sb-=b[id[i][j]];sc-=c[id[i][j]];sd-=d[id[i][j]];
}
}
int main()
{
scanf("%d",&test);
while(test--)
{
scanf("%d%d",&n,&k);memset(cnt,0,sizeof(cnt));mx=0;
for (int i=1;i<=n;i++)
{
scanf("%d%d%d%d%d",&t,&a[i],&b[i],&c[i],&d[i]);
mx=max(mx,t);
cnt[t]++;id[t][cnt[t]]=i;
}
ans=-1e18;sa=sb=sc=sd=0;
dfs(1);
printf("%lld\n",ans);
}
return 0;
}
1011
这个题和第一场冲了……导致一个 hard 变成了 medium-easy。考虑令一个物品的两种权值为 ,就转化成了这一题的第一部分。代码就不放第二次了。
1012
刚开始看成了字符串的编辑距离,写了半天发现没过样例……才发现这题水的不行。
表示最大的 使得 和 的 LCS 长度至少为 。直接 转移即可,询问的话找到最大的 使得 即可。复杂度 。
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
const int MAXN = 100005;
int f[MAXN][25][25], n, m, Q, T;
char A[MAXN], B[25];
int main() {
for (scanf("%d", &T); T--;) {
scanf("%s%s", A + 1, B + 1);
n = strlen(A + 1), m = strlen(B + 1);
f[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
f[i][0][0] = i + 1;
for (int j = 1; j <= m; j++) {
f[i][j][0] = i + 1;
if (A[i] == B[j]) for (int k = 1; k <= m; k++)
f[i][j][k] = f[i - 1][j - 1][k - 1];
else for (int k = 1; k <= m; k++)
f[i][j][k] = max(f[i - 1][j][k], f[i][j - 1][k]);
}
}
scanf("%d", &Q);
while (Q--) {
int l, r; scanf("%d%d", &l, &r);
int ans = 0;
for (int k = 1; k <= m; k++)
if (f[r][m][k] >= l) chkmax(ans, k);
else break;
printf("%d\n", r - l + 1 + m - 2 * ans);
}
}
return 0;
}