ZROI 提高十连测 DAY3

由于我不太会写 觉得从比赛开始就冷静分析。然后看完三道题心态有点爆炸没有紧扣题目的性质。

这个心态是不可取的尽量不要有畏难心理 不要草草的写暴力。

LINK:[最长01子序列](http://zhengruioi.com/contest/399/problem/960)

对于一个序列要求最长01子序列 显然不太能写感觉无从下手的样子 不妨简化一下问题 先推出来一些性质。

如果求连续的最长01子序列且满足题目中的性质我们显然是根据每一个1进行统计答案。我们直接扫一遍即可。

如果是序列呢 我们发现这个序列两个1之间相差的x是关键 因为没有这个x我们不知道自己算的是哪一种。

这个比较暴力的做法是暴力枚举x 然后统计答案 如何统计答案?显然的有贪心前面的0能选就选了这样显然对后面会更优的。

不那么暴力的做法 事实上我有一个假做法 当时以为这是一个单峰函数 但是结果并非如此因为我无法证明这是单峰函数。

实际上也并非单峰函数 因为这个某个x与其相关的y的值和1和0的个数以及其分布位置是有关的 所以这并非单峰函数。(脑残写了一个三分

害怕超时所以把1抽了出来然后预处理了两个数组 暴力枚举x 然后贪心的判断 然后A掉了这道题。

说起来 这个也算是极大的优化吧。考虑正解:正解和上述做法有异曲同工之妙但是复杂度是有保证的。

都是暴力枚举 然后 匹配的话为了为了每次匹配到下一个1决定二分寻找下一个1 显然对于我们枚举的x 1的个数为n/x

那么我们要二分1个个数次 所以 1个数为n/1+n/2+..n/n显然根据调和级数我们1的个数最多有lnn个所以总复杂度为nlnnlogn

还算很好写.. ``` //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INF 2147483646 #define ll long long #define R register using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=100010; int n,last,top,ans,mark; char a[MAXN]; int p[MAXN],w[MAXN]; int pos[MAXN]; inline int calc(int w,int x) { if(p[top]-p[w]<=x)return top+1; int l=w+1,r=top; while(l>1; if(p[mid]-p[w]>=x)r=mid; else l=mid+1; } return r; } inline int solve(int x) { int cnt=0,st=0;mark=0; while(1) { int s=calc(st,x); if(s==top+1) { if(w[st]>=x) { cnt+=x; break; } else { --mark; --cnt; break; } } st=s; ++mark; cnt+=x+1; } if(!mark)return 0; return cnt; } int main() { freopen("1.in","r",stdin); scanf("%s",a+1);n=strlen(a+1); last=0; for(int i=1;i<=n;++i) if(a[i]=='1') { pos[++top]=i; p[top]=i-last-1+p[top-1]; last=i; } last=n+1;mark=top; for(int i=n;i>=1;--i) if(a[i]=='1') { w[mark]=last-i-1+w[mark+1]; last=i; --mark; } for(int i=0;i<=n;++i)ans=max(ans,solve(i)); printf("%d\n",ans); return 0; } ```

T2 绝对是一个比较神仙的题目 我是很难想到的解法 把到每一个点的 路径长度压缩到log1.1的级别 这个还是要推出一些性质 但是性质推出来之后那么一切也就很好写了。

对于每次询问我们直接二分即可 对于合并 采用归并可以做到O(n)而sort就显得比较满了复杂度也显得比较高因为复杂度是近乎mlognlogn这个的复杂度甚至会更高一点 T了也...我不清楚别问我

关于性质这个性质我是没有网这个方面去思考 可能思考也思考不出来什么 看题解也看了半天可能是数学没学好 对对数函数一点也不敏感。

性质是这样的对于三个边权 x y z来说 如果满足 $\frac{1}{1.1z}<=x<=y<=z$这个性质 我们可以推出来y是没用的。

为什么?为什么?对于一个询问dis考虑三种情况:

$dis<=\frac{1}{1.1z}$此时z一定是不满足条件的x比y更满足条件 等式变一下显然

$\frac{1}{1.1z}<=dis<=y$此时z一定是满足条件的 此时不需要y

$dis>y$ 那么y显然是不符合条件的。综上y真没用。

所以说log1.1(maxdis)是距离总数。当然写完了还是很虚 觉得这个东西不是我自己推出来的非常不真切 也对log1.1的值没有大概的把握所以比较自闭。

然而我 思考了一晚上还是觉得不真切 因为这个式子真的很难列出来 但是这个方向的优化还是可以写的。 ``` const ll MAXN=200010; ll n,m,Q,t,h,len; ll ru[MAXN],q[MAXN],tmp[MAXN]; ll f[MAXN][500],flag[MAXN]; ll lin[MAXN],ver[MAXN],nex[MAXN],e[MAXN]; inline void add(ll x,ll y,ll z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline ll get_x(ll x){return x+x/10ll;} inline void modify(ll x,ll y,ll z) { if(!flag[x])return; ll i=1,j=1,cnt=0; for(ll k=1;k<=flag[x]+flag[y];++k) { if((i<=flag[x]&&f[x][i]+z<=f[y][j])||j>flag[y]) { tmp[++cnt]=f[x][i]+z; ++i; } else tmp[++cnt]=f[y][j],++j; } f[y][flag[y]=1]=tmp[1];f[y][0]=-200; for(ll i=2;i<=cnt;++i) { if(tmp[i]<=get_x(f[y][flag[y]-1]))f[y][flag[y]]=tmp[i]; else f[y][++flag[y]]=tmp[i]; } } inline void topsort() { while(h++>1; if(f[x][mid]>=y)r=mid; else l=mid+1; } ll w=get_x(y); if(r!=flag[x]+1&&f[x][r]<=w)return 1; return 0; } int main() { freopen("1.in","r",stdin); n=read();m=read();Q=read(); for(ll i=1;i<=m;++i) { ll x,y,z; x=read();y=read();z=read(); add(x,y,z);++ru[y]; } f[1][++flag[1]]=0; for(ll i=1;i<=n;++i)if(!ru[i])q[++t]=i; topsort(); for(ll i=1;i<=Q;++i) { ll x,y; x=read();y=read(); ll ans=ask(x,y); if(ans)puts("YES"); else puts("NO"); } return 0; } ``` T3 是一个比较难写的码农题决定想好了所有的细节再决定实现。

posted @ 2019-09-15 06:32  chdy  阅读(262)  评论(0编辑  收藏  举报