noip29

T1

以下的LIS代指最长不降子序列。

考场看到取模,便想到了之前写过的Medain,取模操作让序列分布均匀,对应到本题上,既然是求LIS,那它应该是有循环节的,后来打表证实确实是有。

然后,我码了个BIT优化LIS。觉得应该能拿30pts,然后就傻逼的跳了,其实正解已经想的差不多了,但是本着先把暴力分都拿到的原则,就跳了 我是sb

考场想法:

既然有循环节,那么就只需要把整个序列拆成三部分分别为A,B,C,其中A为包含一个循环节的最短的那一段序列,B全是循环的,C是最后那一段不完全的循环节,如果有的话。

那么就只需要对A求一下LIS,并记录最优答案是从那个位置转移过来的,多个的话全都记,然后看B中有多少个循环节,就对答案产生多少贡献,最后再对记录下来的求一遍LIS,然后对应到C上,看能拿多少。

看似正确,然而我没细想,假了,或者说我的打法假了,比如下面这组数据:

12
134 33 107 69 41

生成序列为:

134 32 16 20 34 35 0 28 32 16 20 34

很显然,LIS长度为5,但我的会输出4,因为它在A中求了个4,选的是 16,20,34,35,导致在C中选不了,但显然,最优应该是在A中选16,20,28,然后在C中32,34,LIS长度为5。

然而考试的时候也没调出来,30pts的也没交上,只能说是活该吧

正解:

就是找循环节,然后一顿乱搞,把循环节接150遍,其他的贡献都是1。

我的那份调了半个下午没调出来,所以换了做法

pdf上的矩阵快速幂不会,也懒的粘了,就这样吧。

Code
#include<cstdio>
#define MAX 1000010
#define re register
namespace OMA
{
   long long n,ans;
   int t,a,b,c,d,m,len,xam;
   int tmp[MAX],pos[MAX];
   inline int max(int a,int b)
   { return a>b?a:b; }
   class BIT
   {
     private:
     int tree[MAX];
     inline int lowbit(int x)
     { return x&-x; }
     public:
     inline void insert(int x,int lis)
     {
       for(re int i=x; i<=xam; i+=lowbit(i))
       { tree[i] = max(tree[i],lis); }
     }
     inline int query(int x)
     {
       int res = 0;
       for(re int i=x; i; i-=lowbit(i))
       { res = max(res,tree[i]); }
       return res;
     }
   }BIT;
   signed main()
   {
     scanf("%lld%d%d%d%d%d",&n,&t,&a,&b,&c,&d);
     if(n==1)
     { printf("1\n"); return 0; }
     tmp[1] = t;
     for(re int i=2; i<=n; i++)
     {
       xam = max(xam,tmp[i] = (a*tmp[i-1]*tmp[i-1]+b*tmp[i-1]+c)%d);
       if(pos[tmp[i]])
       { len = i-pos[tmp[i]],m = i-1; break ; }
       pos[tmp[i]] = i;
     }
     xam += 1;
     ans = (n-m)/len-150,n = (n-m)%len+len*150+m;
     for(re int i=m+1; i<=n; i++)
     { tmp[i] = tmp[pos[tmp[m+1]]+i-m-1]; }
     int lis = 0;
     for(re int i=1; i<=n; i++)
     {
       tmp[i] += 1; 
       int res = BIT.query(tmp[i])+1;
       BIT.insert(tmp[i],res);
       lis = max(lis,res);
     }
     printf("%lld\n",ans+lis);
     return 0;
   }
}
signed main()
{ return OMA::main(); }

算了,还是贴一下,虽然没有意义

矩阵快速幂解法

image

image

image

T2

一看就不太可做,留在了最后,瞎打了个背包就没看了,然后接着去调T1了。

别人的想法:

同余最短路,能拿90pts,但好像是假的,然而这玩意我都听都没听过,更别提考场写出来

正解:

%%%沈学长。

是个dp,还没写出来,所以先咕了。

dp实质上是在一个图上跑,平常的dp都是有拓扑序,所以可以直接for循环来转移,想这种出现环的,需要跑个最短路来转移。

Code
咕咕咕

T3

考场直接树剖+线段树,然后打着打着发现思路卡壳了,没继续往下想,因为T1自己的想法还没打,所以干脆拿树剖求了个LCA,丢了个暴力就跑回T1了。

正解:

就是个线段树。

首先不难发现,一个节点 \(u\) 的权值,只有可能对自己的子树产生贡献,所以当一个节点 \(u\) 被修改之后,先用 \(u\) 的权值去更新 \(u\) 的子树,然后去暴力跳 \(u\) 的爹 \(fa\) ,那么\(fa\) 的子树中,除去 \(u\) 的子树那一部分都可以用 \(fa\) 的权值去更新,如果 \(fa\) 的子树在修改前就已经有黑点的话,就说明 \(fa\) 的父亲们或者说祖先之前肯定更新过了子树,就不再接着跳了。

说白了就是区间修改+单点查询

Code
#include<cstdio>
#include<cstring>
#include<climits>
#define MAX 100010
#define re register
#define INF INT_MIN
int n,m;
int w[MAX];
bool vis[MAX];
struct graph
{
  int next;
  int to;
}edge[MAX<<1];
int cnt=1,head[MAX];
inline void add(int u,int v)
{ edge[++cnt] = (graph){head[u],v},head[u] = cnt; }
namespace FTC
{
   int fa[MAX],size[MAX],dfn[MAX];
   inline void dfs(int u,int fat)
   {
     size[u] = 1,dfn[u] = ++cnt,fa[u] = fat;
     for(re int i=head[u],v; i; i=edge[i].next)
     {
       v = edge[i].to;
       if(v!=fat)
       { dfs(v,u); size[u] += size[v]; }
     }
   }
}using namespace FTC;
namespace OMA
{
   inline int max(int a,int b)
   { return a>b?a:b; }
   class Segment_Tree
   {
     private:
     struct TREE
     {
       int xam;
       int l,r;
       int tag;
       TREE()
       { xam = -1; }
     }st[MAX<<2];
     inline int ls(int p)
     { return p<<1; }
     inline int rs(int p)
     { return p<<1|1; }
     inline void Push_up(int p)
     { st[p].xam = max(st[ls(p)].xam,st[rs(p)].xam); }
     inline void Push_down(int p)
     {
       if(st[p].tag)
       {
         st[ls(p)].xam = max(st[ls(p)].xam,st[p].tag);
         st[ls(p)].tag = max(st[ls(p)].tag,st[p].tag);
         st[rs(p)].xam = max(st[rs(p)].xam,st[p].tag);
         st[rs(p)].tag = max(st[rs(p)].tag,st[p].tag);
         st[p].tag = 0;
       }
     }
     public:
     inline void build(int p,int l,int r)
     {
       st[p].l = l,st[p].r = r;
       if(l==r)
       { 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>r)
       { return ; }
       if(l<=st[p].l&&st[p].r<=r)
       { st[p].xam = max(st[p].xam,val); st[p].tag = max(st[p].tag,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); }
       Push_up(p);
     }
     inline int query(int p,int pos)
     {
       if(st[p].l==st[p].r)
       { return st[p].xam; }
       Push_down(p);
       int ans = INF,mid = (st[p].l+st[p].r)>>1;
       if(pos<=mid)
       { ans = max(ans,query(ls(p),pos)); }
       else
       { ans = max(ans,query(rs(p),pos)); }
       return ans;
     }
     inline void modify(int u)
     {
       update(1,dfn[u],dfn[u]+size[u]-1,w[u]);
       while(!vis[u]&&fa[u])
       {
         vis[u] = 1;
         update(1,dfn[fa[u]],dfn[u]-1,w[fa[u]]);
         update(1,dfn[u]+size[u],dfn[fa[u]]+size[fa[u]]-1,w[fa[u]]);
         u = fa[u];
       }
     }
   }Tree;
   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;
   }
   signed main()
   {
     n = read(),m = read();
     for(re int i=1; i<=n; i++)
     { w[i] = read(); }
     for(re int i=1,u,v; i<=n-1; i++)
     { u = read(),v = read(); add(u,v),add(v,u); }
     cnt = 0,dfs(1,0);
     Tree.build(1,1,n);
     for(re int i=1,u; i<=m; i++)
     {
       char s[10]; scanf("%s",s),u = read();
       if(s[0]=='M')
       { Tree.modify(u); }
       if(s[0]=='Q')
       { printf("%d\n",Tree.query(1,dfn[u])); }
     }
     return 0;
   }
}
signed main()
{ return OMA::main(); }

反思总结:

打暴力不要占用太长时间,不然留给调试可能是正解的代码的时间会不太够,导致出现问题,打完暴力后,也要先把暴力交上,给自己留条后路。

不要去搞极限操作,指在距离考试结束还有7s交代码,rp不好,就会挂掉,是极大概率挂掉

有想法就要赶快码出来,不要放到最后。

posted @ 2021-08-04 06:55  -OMA-  阅读(91)  评论(1编辑  收藏  举报
浏览器标题切换
浏览器标题切换end