noip模拟15
这次再次刷新排名下限,遭遇了严重挂分后掉出了前20
\(t1\) 因为没选对编译器+精度损失挂40分暴力,\(t2\) 因为更智障的错误挂 55 分……
int 类型的 dfs1 调用以后没赋值暴毙……
cyh 巨佬暴切 \(t3\) \(rk1\),ys 巨佬暴切 \(t2\)
考场
没睡醒看 \(t1\),打了个暴力找规律,试图用欧拉函数,浪费了一个小时
看 \(t2\) 没思路,再看 \(t3\),发现好像胡了一个比较正确但是较为难写的做法,于是赶紧回去把 \(t2\) 暴力打完连大样例都没测直接开始打 \(t3\),还有一个小时的时候打完,一遍过样例,开始很自信地以为 A 掉了这道题
发现 \(t3\) 没有大样例,稳妥一点还是写个对拍把
写完没两下就挂了,开始紧张调代码,越调越紧张,直到最后考试结束
于是整场考试唯一得分是对拍打的暴力……
A. 夜莺与玫瑰
又是一个数学神仙题
首先转化细想,只考虑斜率>0的函数,最后 \(ans * 2+n+m\) 即可
对于每种解析式的函数,点阵会有若干点落在上面,那么只统计这条直线上最后一个点即可
对于每个斜率的函数,可以由 \((a,b)\) 唯一表示,其中 \(gcd(a,b)=1\)
那么总数即是 \((n-a)(m-b)-max(n-2a,0) * max(n-2b,0)\)
理解一下:总的点数减去中间部分的点
那么最后的答案即为
观察后面的 max 很不舒服,发现当 \(a\le n\) 的时候才会有值,所以继续化简:
然后发现这个 \(b\) 和 \(gcd\) 很烦人,思考一下发现 \((n-a)*m\) 这个东西出现个数只和满足条件的 \(b\) 的个数有关,所以处理数组 \(tot[a][m]\) 表示 \([1,m]\) 区间内与 \(a\) 互质的数的个数
后面 \((n-a)b\) 表述出来相当于求与 \(a\) 互质的所有数的和,类似地处理一个 \(sum\) 数组
于是对于每组询问 \(O(n)\) 回答即可
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=4015;
const int MAX=4001;
const int mod=(1<<30);
int sum[maxn][maxn],n,m,t;
short g[maxn][maxn],tot[maxn][maxn];
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
int main(){
// printf("%d\n",sizeof(int));
for(int i=1;i<=MAX;i++){
g[i][i]=g[i][0]=g[0][i]=i;
for(int j=1;j<i;j++){
g[i][j]=g[j][i]=g[j][i%j];
}
}
for(int i=1;i<=MAX;i++){
for(int j=1;j<=MAX;j++){
tot[i][j]=tot[i][j-1];
sum[i][j]=sum[i][j-1];
if(g[i][j]==1){
tot[i][j]++;
sum[i][j]=(sum[i][j]+j)%mod;
}
}
}
t=read();
while(t--){
n=read();
m=read();
long long ans=0;
for(int i=1;i<n;i++){
ans=(ans+(n-i)*(tot[i][m]*m%mod-sum[i][m])%mod)%mod;
if(i*2<n)ans=ans-(n-2*i)*(m*tot[i][m/2]%mod-2*sum[i][m/2]%mod)%mod;
}
ans=((ans*2%mod+n+m)%mod+mod)%mod;
printf("%lld\n",ans);
}
return 0;
}
B. 影子
可以统计过每个点的以这个点为最小值时的贡献,可以发现,如果按照点权排序,从小到大枚举点,统计完路径后,可以将点删除,将连通块裂为数个连通块
但是发现分裂的操作非常不好维护,可以转化思维,将分裂变为合并,合并操作就可以用并查集轻松维护了
每次合并相当于求连通块内的直径乘以点权统计贡献(如果直径不过当前点也没有关系, 因为这条直径一定已经在某个点权更大的点上统计过贡献,那么这个点不会产生影响)
并查集维护连通性、连通块直径长度、端点即可,用维护直径的经典套路合并
代码实现
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=3e5+5;
const int maxm=6e5+5;
int tt,n,m,cnt,hd[maxn],f[maxn],fa[maxn],dep[maxn],dis[maxn],d[maxn],ans,s[maxn],t[maxn],x,y,w,tp[maxn],son[maxn],siz[maxn],b[maxn];
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
struct P{
int val,id;
}a[maxn];
bool cmp(P a,P b){
return a.val>b.val;
}
struct Edge{
int nxt,to,val;
}edge[maxm];
void add(int u,int v,int w){
edge[++cnt].nxt=hd[u];
edge[cnt].to=v;
edge[cnt].val=w;
hd[u]=cnt;
return ;
}
void dfs(int u){
siz[u]=1;
for(int i=hd[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa[u])continue;
dep[v]=dep[u]+1;
dis[v]=dis[u]+edge[i].val;
fa[v]=u;
dfs(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
return ;
}
void dfs1(int u,int top){
tp[u]=top;
if(!son[u])return ;
dfs1(son[u],top);
for(int i=hd[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa[u]||v==son[u])continue;
dfs1(v,v);
}
return ;
}
int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
int lca(int x,int y){
while(tp[x]!=tp[y]){
if(dep[tp[x]]<dep[tp[y]])swap(x,y);
x=fa[tp[x]];
}
if(dep[x]>dep[y])swap(x,y);
return x;
}
int calc(int x,int y){
int k=lca(x,y);
return dis[x]+dis[y]-2*dis[k];
}
void merge(int x,int y){
int mx=0,ss,tt;
if(d[x]>mx){
mx=d[x];
ss=s[x];
tt=t[x];
}
if(d[y]>mx){
mx=d[y];
ss=s[y];
tt=t[y];
}
if((w=calc(s[x],s[y]))>mx){
mx=w;
ss=s[x];
tt=s[y];
}
if((w=calc(s[x],t[y]))>mx){
mx=w;
ss=s[x];
tt=t[y];
}
if((w=calc(t[x],s[y]))>mx){
mx=w;
ss=t[x];
tt=s[y];
}
if((w=calc(t[x],t[y]))>mx){
mx=w;
ss=t[x];
tt=t[y];
}
s[x]=ss;
t[x]=tt;
d[x]=mx;
f[y]=x;
return ;
}
signed main(){
//freopen("1.in","r",stdin);
tt=read();
while(tt--){
ans=0;
memset(hd,0,sizeof hd);
cnt=0;
memset(fa,0,sizeof fa);
memset(son,0,sizeof son);
n=read();
for(int i=1;i<=n;i++){
b[i]=a[i].val=read();
a[i].id=i;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n-1;i++){
x=read();
y=read();
w=read();
add(x,y,w);
add(y,x,w);
}
// cout<<"hhh "<<endl;
// cout<<"hhh ";
dfs(1);
// cout<<"hhh "<<endl;
dfs1(1,1);
// cout<<"hhh "<<endl;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++){
f[i]=i;
s[i]=t[i]=i;
d[i]=0;
}
// cout<<calc(2,3)<<endl;
for(int j=1;j<=n;j++){
int u=a[j].id;
for(int i=hd[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(find(u)!=find(v)&&b[v]>=b[u])merge(find(u),find(v));
}
ans=max(ans,d[find(u)]*a[j].val);
}
cout<<ans<<endl;
}
return 0;
}
/*
1
3
1 2 3
1 2 1
1 3 2
*/
C. 玫瑰花精
考场上看出来是线段树,只不过我的思路有亿点点奇怪
可以把所有花精位置丢进一个 \(set\) 里,每次插入后可以快速找到前一个和后一个的位置
用线段树动态维护里所有花精最远的位置,每次插入一个花精后,对于前后分别加一个公差为1或-1的等差数列(这个操作由于过于阴间,特别难写……)
考场调了1小时+ 也没调处来
考后改正智障错误自信提交,发现T了,发现 \(set\) 时间复杂度太高,于是又手写了一棵权值线段树,最后搞得又臭又长……
正解是 cyh 巨佬的写法,线段树像维护山海经一样维护最长的区间,并动态维护左右端点,貌似特别好写……
update: 并不是 \(set\) 常数大,而是把 \(set\) 放进了 \(lower\_bound\) 里……
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
const int inf=0x3f3f3f3f;
int n,m,op,x,pos[maxn],ll,rr,wz[maxn];
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
set<int>s;
struct Node{
int fi,se;
Node(){}
Node(int x,int y):fi(x),se(y){}
};
struct Node1{
int fi,se;
Node1(){}
Node1(int x,int y):fi(x),se(y){}
};
struct Seg{
int l,r;
Node mx;
Node1 lazy;
}t[maxn*4];
#define ls t[p<<1]
#define rs t[p<<1|1]
void update(int p){
if(ls.mx.fi>=rs.mx.fi)t[p].mx=ls.mx;
else t[p].mx=rs.mx;
return ;
}
void build(int p,int l,int r){
t[p].l=l;
t[p].r=r;
t[p].lazy=Node1(0,0);
if(l==r){
t[p].mx=Node(0,l);
return ;
}
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
update(p);
return ;
}
void spread(int p){
Node1 k=t[p].lazy;
ls.lazy=k;
rs.lazy=Node1(k.fi+(ls.r-ls.l+1)*k.se,k.se);
if(t[p].lazy.se==1){
ls.mx=Node(k.fi+ls.r-ls.l,ls.r);
rs.mx=Node(rs.lazy.fi+rs.r-rs.l,rs.r);
}
else{
ls.mx=Node(k.fi,ls.l);
rs.mx=Node(rs.lazy.fi,rs.l);
}
t[p].lazy=Node1(0,0);
return ;
}
void change(int p,int l,int r,int val,int op,int ori){
if(l>r)return ;
if(t[p].l>=l&&t[p].r<=r){
t[p].lazy=Node1(val,op);
if(op==1){
t[p].mx=Node(val+t[p].r-t[p].l,t[p].r);
}
else{
t[p].mx=Node(val,t[p].l);
}
return ;
}
if(t[p].lazy.se!=0)spread(p);
int mid=t[p].l+t[p].r>>1;
if(l<=mid)change(p<<1,l,r,val,op,ori);
if(r>mid)change(p<<1|1,l,r,ori+op*(mid-l+1),op,ori);
update(p);
return ;
}
namespace S{
struct Seg1{
int l,r,mn,mx;
}t1[maxn*4];
void update1(int p){
t1[p].mn=min(t1[p<<1].mn,t1[p<<1|1].mn);
t1[p].mx=max(t1[p<<1].mx,t1[p<<1|1].mx);
return ;
}
void build1(int p,int l,int r){
t1[p].l=l;
t1[p].r=r;
if(l==r){
wz[l]=p;
t1[p].mn=0x3f3f3f3f;
return ;
}
int mid=l+r>>1;
build1(p<<1,l,mid);
build1(p<<1|1,mid+1,r);
update1(p);
return ;
}
void change1(int p,int pos,int op){
if(t1[p].l==t1[p].r&&t1[p].l==pos){
if(op==1)t1[p].mn=t1[p].mx=pos;
else t1[p].mn=inf,t1[p].mx=0;
return ;
}
int mid=t1[p].l+t1[p].r>>1;
if(pos<=mid)change1(p<<1,pos,op);
else change1(p<<1|1,pos,op);
update1(p);
return ;
}
void ask(int pp){
int p=wz[pp];
while(p>>1){
if(((p>>1)<<1)==p){
if(rr==inf)rr=t1[(p>>1)<<1|1].mn;
}
else{
if(!ll)ll=t1[(p>>1)<<1].mx;
}
p>>=1;
}
return ;
}
}
int main(){
// freopen("shuju.in","r",stdin);
// freopen("my.out","w",stdout);
n=read();
m=read();
build(1,1,n);
S::build1(1,1,n);
for(int i=1;i<=m;i++){
op=read();
x=read();
if(op==1){
pos[x]=t[1].mx.se;
S::change1(1,pos[x],1);
// set<int>::iterator it=s.insert(pos[x]).first;
ll=0;
rr=inf;
S::ask(pos[x]);
int l;
//set<int>::iterator it1=it;
if(!ll)l=1;
else l=(ll+pos[x]+1)>>1;
if(l!=pos[x])change(1,l,pos[x],(pos[x]-l),-1,pos[x]-l);
printf("%d\n",pos[x]);
// it++;
change(1,pos[x],rr!=inf?((pos[x]+rr)>>1):n,0,1,0);
}
else{
ll=0;
rr=inf;
S::ask(pos[x]);
// set<int>::iterator it=upper_bound(s.begin(),s.end(),pos[x]);
//set<int>::iterator it1=it;it1--;//lower_bound(s.begin(),s.end(),pos[x]);
if((!ll)&&(rr==inf)){
build(1,1,n);
}
else{
int l,r;
if(!ll){
r=rr-1;
change(1,1,r,r,-1,r);
}
else if(rr==inf){
l=ll+1;
change(1,l,n,1,1,1);
}
else{
l=ll+1;
r=rr-1;
int mid=l+r>>1;
change(1,l,mid,1,1,1);
change(1,mid+1,r,(r-mid),-1,r-mid);
}
}
S::change1(1,pos[x],-1);
//s.erase(pos[x]);
}
}
return 0;
}
/*
7 11
1 15
1 123123
1 3
1 5
2 123123
2 15
1 21
2 3
1 6
1 7
1 8
1
7
4
2
7
4
1
3
*/