noip21
所以分差到底要不要取绝对值啊
T1
3分钟出暴力,十分钟码好,然后样例过不去...
好吧,我是sb,求中位数之前是要排序的。
直接冲暴力,50pts。
\(w=3\) 的点,开个桶记录一下又有20pts
还有人冲平衡树
正解:
题解曰:
然后注意到这题的输入很诡异,其实可以当做是随机数据
我:????
好吧,其实是我太菜了,取模操作让这个数列分布均匀。
数据范围:\(w\le k\le n\),所以不应该是取模操作吗
既然是随机数据 那么它就满足分布均匀这一性质,所以,中位数的值变化是常数级的我不知道该怎么来描述,所以这句话摘自题解
那么只需要用个指针,维护中位数的位置就好了,对奇偶分类讨论即可。
有个sb坑点,计算 \(s1\) 的时候可能会爆int。
小优化:
素数个数只筛到 \(n\) 个就够了。

具体实现见code。
Code
#include<bitset>
#include<cstdio>
#define re register
const int N = 1e7+10;
const int T = 1.8e8+10;
namespace OMA
{
int n,k,w;
double sum;
int nt[N];
int s1[N],s2[N];
int cnt,pri[N];
std::bitset<T>vis;
inline void opt()
{
for(re int i=2; i<=T,cnt<=n; i++)
{
if(!vis[i])
{ vis[pri[++cnt] = i] = 1; }
for(re int j=1; j<=cnt&&i*pri[j]<=T; j++)
{
vis[i*pri[j]] = 1;
if(!(i%pri[j]))
{ break ; }
}
}
}
int mid[2],pos[2],tmp[2];
signed main()
{
scanf("%d%d%d",&n,&k,&w),opt();
for(re int i=1; i<=n; i++)
{ s1[i] = 1LL*i*pri[i]%w; }
for(re int i=1; i<=n; i++)
{ s2[i] = s1[i]+s1[i/10+1]; }
for(re int i=1; i<=k-1; i++)
{ nt[s2[i]]++; }
if(k&1)
{
mid[0] = k/2+1;
for(re int i=k; i<=n; i++)
{
nt[s2[i]]++;
if(s2[i]<=tmp[0])
{ pos[0]++; }
if(i>k)
{
nt[s2[i-k]]--;
if(s2[i-k]<=tmp[0])
{ pos[0]--; }
}
while(pos[0]<mid[0])
{ pos[0] += nt[++tmp[0]]; }
while(pos[0]>=mid[0]+nt[tmp[0]])
{ pos[0] -= nt[tmp[0]--]; }
sum += tmp[0];
}
}
else
{
mid[0] = k/2,mid[1] = k/2+1;
for(re int i=k; i<=n; i++)
{
nt[s2[i]]++;
if(s2[i]<=tmp[0])
{ pos[0]++; }
if(s2[i]<=tmp[1])
{ pos[1]++; }
if(i>k)
{
nt[s2[i-k]]--;
if(s2[i-k]<=tmp[0])
{ pos[0]--; }
if(s2[i-k]<=tmp[1])
{ pos[1]--; }
}
while(pos[0]<mid[0])
{ pos[0] += nt[++tmp[0]]; }
while(pos[0]>=mid[0]+nt[tmp[0]])
{ pos[0] -= nt[tmp[0]--]; }
while(pos[1]<mid[1])
{ pos[1] += nt[++tmp[1]]; }
while(pos[1]>=mid[1]+nt[tmp[1]])
{ pos[1] -= nt[tmp[1]--]; }
sum += (double)(tmp[0]+tmp[1])/2.0;
}
}
printf("%0.1lf\n",sum);
return 0;
}
}
signed main()
{ return OMA::main(); }
T2
显然,每次取最大值即是最优策略。
用大根堆维护当前集合中的最大值会T
然后就...

题面没说分差是谁减谁,我默认取绝对值,结果是 \(A-B\)
可恶,然而sb出题人
祭奠一下失去的50pts。
50pts
#include<queue>
#include<cstdio>
#define MAX 100010
#define re register
namespace OMA
{
int a[MAX],p;
int n,k,ans[2],tmp;
std::priority_queue<int>q;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline int abs(int a)
{ return a>=0?a:-a; }
signed main()
{
//freopen("node.in","r",stdin);
//freopen("my.out","w",stdout);
n = read(),k = read();
for(re int i=1; i<=n; i++)
{ a[i] = read(); }
for(re int i=1; i<=k; i++)
{
ans[0] = ans[1] = tmp = 0,p = read();
for(re int j=1; j<=p; j++)
{ q.push(a[j]); }
while(!q.empty())
{
ans[tmp] += q.top();
q.pop();
if(a[p+1]>0)
{ q.push(a[++p]); }
tmp ^= 1;
}
printf("%d\n",ans[0]-ans[1]);
//printf("%d\n",abs(ans[0]-ans[1]));
}
return 0;
}
}
signed main()
{ return OMA::main(); }
考虑其他解法。
我们可以开个桶,统计一下当前集合中每个数出现的次数,再用一个变量维护当前集合中的最大值,每回用最大值去更新答案。
考虑如何维护这个最大值。
我们发现,如果新加入的数不小于当前集合中的最大值,在最优策略下,它会直接被拿走,那么最大值就不会被更新。
如果比当前最大值小,我们直接拿最大值去更新答案,更新前判断维护的最大值的桶里是否还有数,如果没有,直接去枚举来更新最大值,然后更新答案。
可以发现,维护的最大值是在不断减小的,那么枚举时的复杂度就会降低,所以时间复杂度 \(O(nk)\)
记得开long long
Code
#include<cstdio>
#define MAX 200010
#define re register
#define LL long long
namespace OMA
{
int n,k,q,to,tmp;
LL ans[2];
int a[MAX],cnt[MAX];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline int max(int a,int b)
{ return a>b?a:b; }
signed main()
{
n = read(),k = read();
for(re int i=1; i<=n; i++)
{ a[i] = read(); }
for(re int i=1; i<=k; i++)
{
q = read()-1;
int times = 0;
ans[0] = ans[1] = tmp = to = 0;
for(re int j=1; j<=q; j++)
{ cnt[a[j]]++,to = max(to,a[j]); }
while(1)
{
if(times==n)
{ break ; }
q++;
if(q<=n&&a[q]>=to)
{ ans[times%2] += a[q],times++; continue ; }
else
{
cnt[a[q]]++;
if(cnt[to]==0)
{
for(re int j=to-1; j; j--)
{ if(cnt[j]){ to = j; break ; } }
}
ans[times%2] += to,cnt[to]--,times++;
}
}
printf("%lld\n",ans[0]-ans[1]);
}
return 0;
}
}
signed main()
{ return OMA::main(); }
还是想骂sb出题人分差不加绝对值
T3
考场乱搜,大样例都过不了,还能搜对俩点?
外加输出0的7pts,t3有15pts搜错了都有8pts
15pts
#include<cstdio>
#define MAX 100010
#define re register
//#define int long long
namespace OMA
{
int n,v,p[MAX];
int come[MAX],ans;
struct graph
{
int next;
int to;
}edge[MAX<<1];
int cnt=1,head[MAX];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline void add(int u,int v)
{
edge[++cnt].next = head[u];
edge[cnt].to = v;
head[u] = cnt;
}
inline int max(int a,int b)
{ return a>b?a:b; }
inline void dfs(int u,int fa,int res,int meet,int now)
{
//printf("u=%d fa=%d rest of v=%d tourist haven meeted=%d gezi=%d\n",u,fa,res,meet,now);
if(!res)
{ ans = max(ans,now-meet); /*printf("ans=%d\n",ans);*/ return ; }
for(re int i=head[u]; i; i=edge[i].next)
{
int v = edge[i].to;
if(v!=fa)
{ dfs(v,u,res-1,meet,now+come[v]-p[u]); }
}
}
signed main()
{
//freopen("node.in","r",stdin);
//freopen("my.out","w",stdout);
n = read(),v = read();
for(re int i=1; i<=n; i++)
{ p[i] = read(); }
for(re int i=1,x,y; i<=n-1; i++)
{
x = read(),y = read();
add(x,y),add(y,x);
}
if(!v)
{ printf("0\n"); return 0; }
for(re int i=1; i<=n; i++)
{
for(re int j=head[i]; j; j=edge[j].next)
{ come[i] += p[edge[j].to]; }
//printf("come[%d]=%d\n",i,come[i]);
}
//dfs(1,0,v-1,p[1],come[1]+p[1]);
for(re int i=1; i<=n; i++)
{ dfs(i,0,v-1,p[i],come[i]+p[i]); }
printf("%d\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
正解:
树形dp,还没改出来 正在改,快了快了,所以先咕了QAQ
话说后两题改了个题面就拿过来了,真的好吗
updated on 7.25
看了战神题解才改出来,方程好麻烦啊啊啊
我们设 \(dp1_{u,i,0/1}\) 表示从 \(u\) 走到 \(u\) 的子树,选了 \(i\) 个,\(u\) 被没被选,0没选,1选,设\(dp2_{u,i,0/1}\) 表示从 \(u\) 的子树走到 \(u\) ,选了 \(i\) 个,\(u\) 被没被选,0没选,1选。
则有:
\]
\]
\]
\]
其中 \(sum_{u}\) 表示 \(u\) 节点的儿子的权值和。
设答案为 \(ans\),则有:
\]
\]
Code
#include<cstdio>
#include<climits>
#define V 110
#define N 100010
#define re register
#define LL long long
#define MIN INT_MIN
namespace OMA
{
int n,v;
struct graph
{
int next;
int to;
}edge[N<<1];
int cnt=1,head[N];
int p[N];
LL ans,sum[N];
LL dp1[N][V][2],dp2[N][V][2];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline void add(int u,int v)
{
edge[++cnt].next = head[u];
edge[cnt].to = v;
head[u] = cnt;
}
inline LL max(LL a,LL b)
{ return a>b?a:b; }
inline void dfs(int u,int fa)
{
for(re int i=head[u]; i; i=edge[i].next)
{
int to = edge[i].to;
if(to!=fa)
{ dfs(to,u); sum[u] += p[to]; }
}
dp1[u][0][1] = dp2[u][0][1] = MIN;
dp2[u][1][1] = max(dp2[u][1][1],sum[u]+p[fa]);
for(re int i=head[u]; i; i=edge[i].next)
{
int to = edge[i].to;
for(re int j=v; ~j; j--)
{
ans = max(ans,max(dp1[to][j][0],dp1[to][j][1])+max(dp2[u][v-j][0],dp2[u][v-j][1]));
ans = max(ans,max(dp2[to][j][0],dp2[to][j][1])+max(dp1[u][v-j][0],dp1[u][v-j][1]+p[fa]-p[to]));
}
for(re int j=1; j<=v; j++)
{
dp1[u][j][0] = max(dp1[u][j][0],max(dp1[to][j][0],dp1[to][j][1]));
dp1[u][j][1] = max(dp1[u][j][1],max(dp1[to][j-1][0],dp1[to][j-1][1])+sum[u]);
dp2[u][j][0] = max(dp2[u][j][0],max(dp2[to][j][0],dp2[to][j][1]));
dp2[u][j][1] = max(dp2[u][j][1],max(dp2[to][j-1][0],dp2[to][j-1][1])+sum[u]+p[fa]-p[to]);
}
}
}
signed main()
{
n = read(),v = read();
for(re int i=1; i<=n; i++)
{ p[i] = read(); }
for(re int i=1,u,v; i<=n-1; i++)
{
u = read(),v = read();
add(u,v),add(v,u);
}
dfs(1,0);
printf("%lld\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
noip21的更多相关文章
- 20210720 noip21
又是原题,写下题解吧 Median 首先时限有 2s(学校评测机太烂,加到 4s 了),可以放心地筛 \(1e7\) 个质数并算出 \(s_2\),然后问题变为类似滑动求中位数.发现 \(s_2\) ...
随机推荐
- buu 简单注册器
一.本身对安卓逆向这块不是很熟悉,为了看这题稍微了解了一下,原来安卓虚拟机解释运行的是dex文件,和java的字节码不一样,然后是smail语法,这块我不会,所以找个了个神器来反编译2333,然后这题 ...
- buu 不一样的flag
一.查壳 二.拖入ida,分析 从这里和51到53行的代码,基本判断这是一个迷宫题,并且是5行5列的一个迷宫.我当时感觉到一个奇怪的地方是 第一个,我自己想明白是因为可能是int型,数字占了4个字节, ...
- Windows软件包管理工具:Scoop
前言 删库跑路后,Windows系统如何快速安装应用程序,部署环境呢? 以前想过这个问题,最近在安装Hugo时发现使用软件包管理工具可以解决这个问题. 阅读建议 首先需要测试下载速度,尝试从官网下载, ...
- 关于SOA和AOP
SOA:面向服务的架构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来.C/S端框架有WPF,服务端应用程序有WCF.asp.net web ...
- Java | 参数传值机制
值传递 java中,方法中所有的参数的都是"值传递",就是传递的是原来值的副本,不是原来的参数,因此,改变不会影响到原来的参数. 基本数据类型参数的传值 传递的都是副本,改变以后不 ...
- 使用Nginx将请求转发至Google Analytics实现后端数据统计
前言 Google Analytics 加载缓慢是本博客在国内访问缓慢的原因之一.虽然通过使用大公司的 ga.js 的 CDN ,可以很大程度上加快加载 ga.js 文件的速度( ga.js 的更新频 ...
- PAT乙级:1077 互评成绩计算 (20分)
PAT乙级:1077 互评成绩计算 (20分) 在浙大的计算机专业课中,经常有互评分组报告这个环节.一个组上台介绍自己的工作,其他组在台下为其表现评分.最后这个组的互评成绩是这样计算的:所有其他组的评 ...
- 【codeforces1058】Vasya and Golden Ticket 枚举+暴力+模拟
#点击传送 题目描述 Recently Vasya found a golden ticket - a sequence which consists of nn digits a1a2-ana1a2 ...
- python开发,注意事项
提高python代码运行效率 1.使用生成器,节约内存.[一边循环一边计算的机制,称为生成器:generator] 例: .如何创建生成器 1.只要把一个列表生成式的[]改成(),就创建了一个gene ...
- 手把手教你玩转HarmonyOS版地图应用开发
一.导读 7月31日,华为HarmonyOS开发者日将在杭州举行.为了方便更多开发者,高德开放平台地图SDK已在业内率先实现鸿蒙化迁移和重构,全面适配HarmonyOS并面向开发者免费发布.开发者可 ...