TJOI2015 day2解题报告

时间:2023-03-09 09:09:19
TJOI2015 day2解题报告

TJOI2015终于写完啦~~~

T1:[TJOI2015]旅游

描述:(BZ没题面只能口述了。。)一个人在一棵树上走,每次从a->b会进行一次贸易(也就是在这条路径上买入物品然后在后面卖出)然后每次经过一个点该点的物品价格会上涨v,求每次贸易的最大获利

很裸的一道树链剖分,就是题目描述太不明白了。。这样就是在某条路径上找到某个点减去后面路径的最小点的值的最大值。可以用线段树的区间合并解决。就是在求答案时的合并答案上方向搞反了查了很久。。。以前也犯过着种错误,以后不能再犯了。。

CODE:

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define maxn 50010
vector<int> e[maxn];
#define pb push_back
int add[maxn],pre[maxn],fa[maxn],pos[maxn];
int n;
inline void bfs() {
static int q[maxn],s[maxn],ch[maxn];
q[]=;
for (int l=,r=,u=q[l];l<=r;u=q[++l])
for (int i=;i<e[u].size();i++)
if (e[u][i]!=fa[u]) {
fa[e[u][i]]=u;
q[++r]=e[u][i];
}
for (int i=n,u=q[n];i;u=q[--i]) {
s[u]++;
s[fa[u]]+=s[u];
ch[fa[u]]=s[ch[fa[u]]]<s[u]?u:ch[fa[u]];
}
int cnt=;
for (int i=,u=q[];i<=n;u=q[++i]) {
if (add[u]!=) continue;
pos[add[u]=++cnt]=u;pre[u]=u;
for (u=ch[u];u;u=ch[u]) {
pos[add[u]=++cnt]=u;pre[u]=pre[fa[u]];
}
}
}
typedef long long ll;
struct b {
ll mx,mn,ans[];
b() {mx=mn=ans[]=ans[]=;}
};
inline b update(b l,b r) {
b ans;
ans.mx=max(l.mx,r.mx);
ans.mn=min(l.mn,r.mn);
ans.ans[]=max(max(l.ans[],r.ans[]),l.mx-r.mn);
ans.ans[]=max(max(l.ans[],r.ans[]),r.mx-l.mn);
return ans;
}
inline b rec(b x) {
swap(x.ans[],x.ans[]);
return x;
}
struct node {
int l,r,lz;b bo;
}t[maxn*];
#define lc (x<<1)
#define rc (lc^1)
#define mid ((l+r)>>1)
int a[maxn];
void build(int x,int l,int r){
t[x].l=l,t[x].r=r;
if (l==r) {
t[x].bo.mx=t[x].bo.mn=a[pos[l]];
t[x].bo.ans[]=t[x].bo.ans[]=;
return ;
}
build(lc,l,mid);build(rc,mid+,r);
t[x].bo=update(t[lc].bo,t[rc].bo);
}
inline void pb(int x) {
if (t[x].lz==) return ;
if (t[x].l!=t[x].r) {
t[lc].lz+=t[x].lz;
t[lc].bo.mn+=t[x].lz;
t[lc].bo.mx+=t[x].lz;
t[rc].lz+=t[x].lz;
t[rc].bo.mn+=t[x].lz;
t[rc].bo.mx+=t[x].lz;
}
t[x].lz=;
}
inline b change(int x,int x1,int y1,int z) {
int l=t[x].l,r=t[x].r;
pb(x);
if (x1<=l&&r<=y1) {
t[x].lz+=z;
t[x].bo.mn+=z;
t[x].bo.mx+=z;
return t[x].bo;
}
b ans;
if (mid>=y1) ans=change(lc,x1,y1,z);
else if (mid<x1) ans=change(rc,x1,y1,z);
else ans=update(change(lc,x1,y1,z),change(rc,x1,y1,z));
t[x].bo=update(t[lc].bo,t[rc].bo);
return ans;
}
inline b set(int x,int y,int z) {
int c[]={x,y};
b ans[];
bool bo[]={,};
while (c[]!=c[]) {
int l=add[c[]]<add[c[]]?:;
if (pre[c[l]]==pre[c[l^]]) {
if (bo[l]) ans[l]=update(change(,add[c[l^]]+,add[c[l]],z),ans[l]);
else ans[l]=change(,add[c[l^]]+,add[c[l]],z);
c[l]=c[l^];
} else {
if (bo[l]) ans[l]=update(change(,add[pre[c[l]]],add[c[l]],z),ans[l]);
else ans[l]=change(,add[pre[c[l]]],add[c[l]],z);
c[l]=fa[pre[c[l]]];
}
bo[l]=;
}
b _ans=change(,add[c[]],add[c[]],z);
if (bo[]) _ans=update(rec(ans[]),_ans);
if (bo[]) _ans=update(_ans,ans[]);
return _ans;
}
int main(){
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
scanf("%d",&n);
for (int i=;i<=n;i++) scanf("%d",a+i);
for (int i=;i<n;i++) {
int x,y;
scanf("%d%d",&x,&y);
e[x].pb(y);e[y].pb(x);
}
bfs();
build(,,n);
int Q;
scanf("%d",&Q);
while (Q--) {
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
b ans=set(x,y,v);
printf("%d\n",ans.ans[],ans.ans[],ans.mx,ans.mn);
}
return ;
}

T2:[TJOI2015]棋盘

就是一道状态压缩+矩阵乘法。

首先我们可以先状压一下,可以发现每次的转移都是相同的,然后就能用矩阵乘法优化了

CODE:

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned int uint;
struct mat{
uint t[][];
mat(){memset(t,,sizeof(t));}
}d,I;
int m,n,p,k,w,a[][];
mat operator *(mat x,mat y) {
mat ans;
for (int i=;i<m;i++)
for (int j=;j<m;j++)
for (int k=;k<m;k++)
ans.t[i][j]+=x.t[i][k]*y.t[k][j];
return ans;
}
mat operator ^ (mat x,int y) {
mat ans=I;
for (;y;y>>=) {
if (y&) ans=ans*x;
x=x*x;
}
return ans;
}
inline bool check(int x,int y){
static bool b[][];
memset(b,,sizeof(b));
for (int i=;i<w;i++) b[][i]=x&(<<i);
for (int i=;i<w;i++) b[][i]=y&(<<i);
for (int i=;i<;i++)
for (int j=;j<w;j++) {
if (!b[i][j]) continue;
for (int u=;u<;u++)
for (int v=;v<p;v++)
if (a[u][v]&&(u!=||v!=k)) {
int x=i+u-,y=j+v-k;
if (x>=&&x<&&y>=&&y<w&&b[x][y]) return ;
}
}
return ;
}
int main(){
freopen("chessboard.in","r",stdin);
freopen("chessboard.out","w",stdout);
scanf("%d%d%d%d",&n,&w,&p,&k);
for (int i=;i<;i++)
for (int j=;j<p;j++) scanf("%d",a[i]+j);
m=<<w;
for (int i=;i<m;i++) I.t[i][i]=;
for (int i=;i<m;i++)
for (int j=;j<m;j++)
d.t[i][j]=check(i,j);
cout<<(d^n+).t[][]<<endl;
return ;
}

T3:[TJOI2015]概率论

这个嘛。。。先贴下官方题解吧

TJOI2015 day2解题报告TJOI2015 day2解题报告TJOI2015 day2解题报告TJOI2015 day2解题报告

好复杂对吧,我们打个表 。。。

f[1]=1/1,f[2]=3/3,f[3]=6/5,f[4]=10/7 ...

找到了吧 f[n]=n*(n+1)/2/(2n-1)。

完了。。。

CODE:

 #include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main(){
int n;
scanf("%d",&n);
printf("%.9lf\n",n*1ll*(n+)/*1.0/(*n-));
return ;
}

话说BZ 350道题了。。。其实这一年几乎都在学校的oj上刷,能在这1年的时间刷多了100道题也挺自豪的。。。想想自己100时的那种兴奋,还是觉得自己挺弱智的。。。

还有3个小时就是自己的17岁生日了,回想自己的第16个年头,还是挺满意的,至少自己坚持了自己的路。在这里祝自己生日快乐,在接下来的一年里会接受越来越大的挑战,或者省队都进不了直接滚粗,或者noi胸牌滚粗,又或者侥幸成为了进队爷。。。不管自己今后咋样,希望能不忘本心吧,自己选择的路,就算跪着也要走完。

距GDOI还有4天,明天就是最后的一场模拟赛了,自己却感觉还总是找不到感觉,真的不想再发生像NOIP还有GDKOI时那黑暗的第二天了。。。自己真的很想被人敬仰膜拜一番,加油吧,再认真仔细一点吧。

以后的路会怎样,就跟我现在在听的歌一样吧。God knows,只有上帝才会知道,我们现在所能做的,只有认真过好每一分每一秒了

GDOI,God Bless!!!