noip34
因为改不动T3而来水博客的屑
昨晚没睡好,大致看了一遍题面后,选择了死亡231,然后就死的很惨。
T1
一开始大致看题面的时候,就略了一眼,加上没读全题,啥思路也没有,最后四十分钟滚回来看了看,发现就是个二分,码好后,发现不太对劲,\(O(n\log n)\) 排序会T,然而不知道该怎么改,就直接交了。
没判0挂了8pts
正解:
就是个二分,二分时间,\(check\) 时计算当前时间各个物品的价值,然后排序,取前m个大,从达到小累加,比 \(s\) 大返回true,否则false。
二分前先判0是否合法,合法直接输出0,不合法再去二分。
然而\(O(n\log n)\) 的 \(sort\) 会T,发现我们只关心前m大的,所以可以用一个叫 \(nth \_element\) 的东西来排序,具体用法:
nth_element(a+head,a+kth,a+tail);
重排 \([head,tail)\) 中的元素,使得 \(kth\) 所指向的元素为拍完序后的该位置出现的数,新的 \(nth\) 元素前所有的元素小于等于新的 \(nth\) 元素后的所有元素。
然后就有个问题,有负的贡献怎么办? 不选不就好了,那它跟0取个 \(\max\) 再排序即可,因为选负的不如不选。
然后就就没了,难点就在于不认识 \(nth\_ element\)
Code
#include<cstdio>
#include<algorithm>
#define MAX 1000100
#define re register
#define int long long
using std::sort;
using std::nth_element;
namespace OMA
{
int n,m,s;
int tmp[MAX];
int ans = 1e9+7;
struct node
{ int k,b; }p[MAX];
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
int w=1; s=0; 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,*this;
}
}cin;
inline int max(int a,int b)
{ return a>b?a:b; }
inline bool cmp(int a,int b)
{ return a>b; }
inline bool check(int mid)
{
for(re int i=1; i<=n; i++)
{ tmp[i] = max(0,mid*p[i].k+p[i].b); }
//sort(tmp+1,tmp+1+n,cmp);
nth_element(tmp+1,tmp+n-m+1,tmp+n+1);
//for(re int i=1; i<=n; i++)
//{ printf("%lld ",tmp[i]); }
//printf("\n");
int sum = 0;
for(re int i=n; i>=n-m+1; i--)
{
sum += tmp[i];
if(sum>=s)
{ return true; }
}
return false;
}
signed main()
{
//freopen("node.in","r",stdin);
cin >> n >> m >> s;
for(re int i=1,k,b; i<=n; i++)
{ p[i] = (node){(cin >> k,k),(cin>>b,b)}; }
if(check(0))
{ printf("0\n"); return 0; }
int l = 0,r = 1e9;
while(l<=r)
{
int mid = (l+r)>>1;
//printf("l=%d r=%d mid=%d\n",l,r,mid);
if(check(mid))
{ r = mid-1; ans = mid; /*printf("%d %d\n",ans,mid);*/ }
else
{ l = mid+1; }
}
printf("%lld\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
记得判0QAQ
T2
一眼看以为高斯消元,看到数据范围直接去世。
短暂的思考发呆犯困之后,选择21pts的特殊性质。
氵完后,试图再搞搞,然而脑子里已经成了浆糊,就滚去T3了。
赛后发现大家都是86pts
正解:
发现对于每个 \(x\) ,都可以表示成 \(x_{i}=k-x_{1}\) 的形式,其中的 \(k\) 是一些 \(w\) 的加加减减,通过手模就可以找到对应的规律,然后就可以方便的回答询问,
- 对于操作1,将表示 \(u,v\) 的式子相加,此时会出现两种情况:
-
\(x_{u}+x_{v}=t\) ,此时只需判断是否 \(s=t\) ,若相等,则\(inf\),否则 \(none\) 。
-
\(x_{u}+x_{v}=t\pm x_{1}\) 然后就可以求得 \(x_{1}\) ,注意题目要求 \(x\) 为整数,不符合条件要输出 \(inf\) 。
- 对于操作二,发现对于节点 \(u\) 的修改,实际上只会影响到 \(u\) 的子树内的信息。
所以就是个区间修改+单点查询,修改时根据点深度的奇偶对应的乘上 \(\pm1\) 即可。
所以用个树状数组来实现,复杂度 \(O((n+q)\log n)\)
如果用线段树的话,是两个 \(\log\) 的,过不了最后一个子任务,卡nm呢
然而还是有人用线段树过了
注意判断解不是整数的情况,以及答案最后要除以2。
86pts
#include<cstdio>
#define MAX 1000010
#define re register
namespace OMA
{
int n,q;
int fa[MAX],w[MAX];
int wei[MAX],dep[MAX];
struct graph
{
int next;
int to;
}edge[MAX<<1];
int cnt=1,head[MAX],id[MAX];
inline void add(int u,int v)
{ edge[++cnt] = (graph){head[u],v},head[u] = cnt; }
struct Segment_Tree
{
struct TREE
{
int val;
int l,r;
int lazy;
}st[MAX<<2];
inline int ls(int p)
{ return p<<1; }
inline int rs(int p)
{ return p<<1|1; }
inline void Push_down(int p)
{
if(st[p].lazy)
{
int opt1 = dep[id[st[ls(p)].l]]&1?1:-1;
int opt2 = dep[id[st[rs(p)].l]]&1?1:-1;
st[ls(p)].val += st[p].lazy*opt1;
st[ls(p)].lazy += st[p].lazy;
st[rs(p)].val += st[p].lazy*opt2;
st[rs(p)].lazy += st[p].lazy;
st[p].lazy = 0;
}
}
inline void build(int p,int l,int r)
{
st[p].l = l,st[p].r = r;
if(l==r)
{ st[p].val = wei[id[l]]; return ; }
int mid = (l+r)>>1;
build(ls(p),l,mid),build(rs(p),mid+1,r);
}
inline void update(int p,int l,int r,int val)
{
if(l<=st[p].l&&st[p].r<=r)
{ st[p].val += val*(dep[id[st[p].l]]&1?1:-1),st[p].lazy += val; return ; }
Push_down(p);
int mid = (st[p].l+st[p].r)>>1;
if(l<=mid)
{ update(ls(p),l,r,val); }
if(r>mid)
{ update(rs(p),l,r,val); }
}
inline int query(int p,int pos)
{
if(st[p].l==st[p].r)
{ return st[p].val; }
Push_down(p);
int ans,mid = (st[p].l+st[p].r)>>1;
if(pos<=mid)
{ return query(ls(p),pos); }
else
{ return query(rs(p),pos); }
}
}Tree;
int dfn[MAX][2];
inline void dfs(int u)
{
dfn[u][0] = ++cnt;
dep[u] = dep[fa[u]]+1;
wei[u] = w[u]-wei[fa[u]];
id[dfn[u][0]] = u;
for(re int i=head[u],v; i; i=edge[i].next)
{ v = edge[i].to; dfs(v); }
dfn[u][1] = cnt;
}
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
int w=1; s=0; 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,*this;
}
}cin;
signed main()
{
//freopen("node.in","r",stdin);
//freopen("my.out","w",stdout);
cin >> n >> q;
for(re int i=2; i<=n; i++)
{ cin >> fa[i] >> w[i]; add(fa[i],i); }
cnt = 0; dfs(1); Tree.build(1,1,n);
for(re int i=1,opt; i<=q; i++)
{
cin >> opt;
if(opt==1)
{
int u,v,s;
cin >> u >> v >> s;
int opt1 = dep[u]&1,opt2 = dep[v]&1;
int val = Tree.query(1,dfn[u][0])+Tree.query(1,dfn[v][0]);
//printf("val1=%d val2=%d s=%d\n",val1,val2,s);
//printf("%d %d\n",dep[u],dep[v]);
if(opt1==opt2)
{
if((s-val)&1)
{ printf("none\n"); }
else
{ printf("%d\n",(opt1?1:-1)*(s-val)/2); }
}
else
{
if(val==s)
{ printf("inf\n"); }
else
{ printf("none\n"); }
}
}
if(opt==2)
{
int u,val;
cin >> u >> val;
//printf("%d %d\n",dfn[u][0],dfn[u][1]);
if(dep[u]&1)
{ Tree.update(1,dfn[u][0],dfn[u][1],val-w[u]); }
else
{ Tree.update(1,dfn[u][0],dfn[u][1],w[u]-val); }
w[u] = val;
}
}
return 0;
}
}
signed main()
{ return OMA::main(); }
话说我树状数组写错调了好久,要死
100pts
#include<cctype>
#include<cstdio>
#define MAX 1000010
#define re register
#define int long long
namespace OMA
{
int n,q,opt[MAX];
int fa[MAX],w[MAX];
int wei[MAX],dep[MAX];
struct graph
{
int next;
int to;
}edge[MAX];
int cnt=1,head[MAX],id[MAX];
inline void add(int u,int v)
{ edge[++cnt] = (graph){head[u],v},head[u] = cnt; }
struct BIT
{
int tree[MAX];
inline int lowbit(int x)
{ return x&-x; }
inline void update(int x,int y)
{
for(re int i=x; i<=n; i+=lowbit(i))
{ tree[i] += y; }
}
inline void build()
{
for(re int i=1; i<=n; i++)
{ update(i,wei[id[i]]*opt[id[i]]-wei[id[i-1]]*opt[id[i-1]]); }
}
inline int query(int x)
{
int res = 0;
for(re int i=x; i; i-=lowbit(i))
{ res += tree[i]; }
return res*opt[id[x]];
}
}BIT;
int dfn[MAX][2];
inline void dfs(int u)
{
dfn[u][0] = ++cnt;
id[dfn[u][0]] = u;
dep[u] = dep[fa[u]]+1;
wei[u] = w[u]-wei[fa[u]];
for(re int i=head[u],v; i; i=edge[i].next)
{ v = edge[i].to; dfs(v); }
dfn[u][1] = cnt;
}
struct stream
{
char buf[1<<21],*p1=buf,*p2=buf;
#define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?(-1):*p1++
template<typename type>inline stream &operator >>(type &s)
{
int w=0; s=0; char ch=gc();
while(!isdigit(ch)){ w |= ch=='-'; ch=gc(); }
while(isdigit(ch)){ s = (s<<1)+(s<<3)+(ch^48); ch=gc(); }
return s = w?-s:s,*this;
}
}cin;
signed main()
{
cin >> n >> q;
for(re int i=2; i<=n; i++)
{ cin >> fa[i] >> w[i]; add(fa[i],i); }
cnt = 0; dfs(1);
for(re int i=1; i<=n; i++)
{ opt[i] = dep[i]&1?1:-1; }
BIT.build();
for(re int i=1,opt1; i<=q; i++)
{
cin >> opt1;
if(opt1==1)
{
int u,v,s;
cin >> u >> v >> s;
int val = BIT.query(dfn[u][0])+BIT.query(dfn[v][0]);
if(opt[u]==opt[v])
{
if((s-val)&1)
{ printf("none\n"); }
else
{ printf("%lld\n",opt[u]*(s-val)/2); }
}
else
{
if(val==s)
{ printf("inf\n"); }
else
{ printf("none\n"); }
}
}
if(opt1==2)
{
int u,val;
cin >> u >> val;
BIT.update(dfn[u][0],opt[u]*(val-w[u]));
BIT.update(dfn[u][1]+1,-opt[u]*(val-w[u]));
w[u] = val;
}
}
return 0;
}
}
signed main()
{ return OMA::main(); }
T3
考场硬刚,啥也没写出来,暴力也写假,爬了爬了。
正解:
树状数组+扫描线?
不会,咕了
反思总结:
-
睡好觉....
-
注意开题顺序,题面读全,不要忽略数据范围。
-
不要硬刚一道题,时间分配要合理。