ZR 提高十连 DAY 4
哇 这题目怎么一次比一次毒瘤 当然这次还好 有会做的题目。
T1 一眼看上去 毒瘤!再看一眼 我真不想看了 扔了。
T2 哇感觉能写 哇这不是 随便都有40分了么 二分?优化一下65到手了。然后剩下的不会了。
T3 爆搜?怎么连30分都没有啊 还是状压吧 只有30分觉得剩下的是贪心就没有再思考了。
先从T2 开始改吧。
LINK:小 (其实就是一个-1 1序列 求出一段连续的序列使其最长。
当然这是转化过的模型了 具体看题目 首先 从答案的角度进行分析 得到最后一定是某个质因数留到了最后 发现没有质因数就没有答案所以要枚举质因数。
枚举完质因数就是上述的问题了 怎么解决?显然我有一个二分的思路然后 复杂度 nlogn 而质因数很多 设为T T=1e6/ln1e6 这个少说也有1e5 所以说 不能这样搞,复杂度过大。
但是由于1的位置非常至少我们不妨将其抽出来。但是通过二分可以发现答案可能不在1这个位置上。但是我们显然可以得到一个推论。
答案的区间一定是一个偶数除非是1~n.这个区间,所以我们得到一对 i j的区间是合法的 那么我们也同时得到了这个区间的长度2*(i-j+1);于是做一些细节的处理就可以得到我们要求的区间辣。
还是一样的结果 枚举所有的质因数来寻找答案 我们分析 一下如何只遍历1的位置来寻找答案。
对于一个i 来说我们要寻找一个尽可能小的j 使得其满足 2*(i-j+1)>=(pi-pj+1) 当然这个地方 i pi 这个玩意都是同时单调 的所以我们还是要找到一个尽量小的j。
这个问题 可以很快的得到答案 。首先 我们得到了这个东西有一些性质 不妨可以讨论一下可以发现pi是单调递增的 并非原来最基础的二分模型每个位置上的数字都不单调。
当然 这个和我的二分做法不尽相同所以 这里不再赘述 我突然有点sb...
我们把1抽出来之后考虑如何求答案 也就是对于每个i如何寻找j 暴力枚举?复杂度还是会达到n^2 我们观察上述的式子可以简化为 pi-2*i-1<=pj-2*j;
显然我们可以排序 然后 就可以通过双指针扫描的方法寻找到最小的j 二分复杂度就有点高了...这样复杂度最好还是一个nlogn的加上了一些小常数罢了。
还是有点迷不过没有关系 正序枚举i好像会出现一些事故 i单调递增 那么这将意味着j的集合不断减小 不妨倒序枚举i 这样j这个集合也好加入..
考虑 了一些细节却反过来迷惑了自己qwq 其实都是一些不必要的情况 如 j找到了i怎么办 我们维护一个最小的j尽管遇到了这种情况也是不会累计贡献的 所以是合法的。
考虑 到 答案所在 最小左端点显然 我们必然能直接从左端点向右边找最小 因为 如果可能不合法那么必然的我们最小的左端点还能更小所以假设不成立。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned 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 ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=500010,maxn=1000010; int n,flag,maxx=1000000,mark; int ans,ansl,top; int a[MAXN],w[MAXN]; vector<int>p[maxn],id[maxn]; struct wy{int p,v,cnt;}t[MAXN];//pos v inline int cmp(wy a,wy b){return a.v>b.v;} inline void upd(int L,int len) { if(len>ans)ansl=L,ans=len; else if(len==ans)ansl=min(ansl,L); return; } int main() { //freopen("1.in","r",stdin); n=read();mark=n/2;if(n&1)++mark; for(int i=2;i<=maxx;++i) if(!p[i].size()) for(int j=i;j<=maxx;j+=i) p[j].push_back(i); for(int i=1;i<=n;++i) { a[i]=read(); if(a[i]!=1)flag=1; for(un int j=0;j<p[a[i]].size();++j) { id[p[a[i]][j]].push_back(i); //cout<<p[a[i]][j]<<endl; if(id[p[a[i]][j]].size()>=mark)mark=INF; } } if(!flag){printf("0 0\n");return 0;} if(mark==INF){printf("1 %d\n",n);return 0;} for(int i=2;i<=maxx;++i) { if(!id[i].size())continue; top=0; for(un j=0;j<id[i].size();++j) { ++top; t[top]=(wy){id[i][j],id[i][j]-2*top,top}; } sort(t+1,t+1+top,cmp); int j=1,now=n+1; for(int k=1;k<=top;++k)//寻找一个j使得 t[k].top-2*t[top].p<=。。。 { while(j<=top&&t[j].v+1>=t[k].v) { now=min(now,t[j].cnt); ++j; } int len=2*(t[k].cnt-now+1); int l=max(1,t[k].p-len+1); upd(l,len); } } printf("%d %d\n",ansl,ansl+ans-1); return 0; }
当然二分也是可以过的(说的不是我的二分 但是我的二分加入一些奇技淫巧也可以过嘿嘿嘿...比如只判断40个质数直接水过有没有 当然这种做法相当的不严谨 我杜绝这样的做法这里我放一个 65分的二分 很巧妙的.
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #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=500010,maxn=1000010; int n,maxx,ans,ansl,L; int a[MAXN],f[MAXN]; int p[maxn]; int w[MAXN]; inline void fj(int x) { for(int i=2;i*i<=x;++i) { if(x%i==0) { while(x%i==0)x=x/i; p[i]=1; maxx=max(maxx,i); } } if(x>1)p[x]=1; maxx=max(maxx,x); } inline int check(int x) { int minn=INF,mark; for(int i=1;i<x;++i)f[i]=f[i-1]+w[i]; for(int i=x;i<=n;++i) { f[i]=f[i-1]+w[i]; if(f[i-x]<minn){minn=f[i-x];mark=i-x+1;} if(f[i]-minn>=0){L=mark;return 1;} } return 0; } inline void solve(int x) { for(int i=1;i<=n;++i) { w[i]=0; if(a[i]%x==0)w[i]=2; --w[i]; } int l=ans,r=n; while(l+1<r) { int mid=(l+r)>>1; if(check(mid))l=mid; else r=mid; } if(check(r)) { if(ans==r){ansl=min(ansl,L);} if(r>ans){ans=r;ansl=L;} } else { check(l); if(ans==l){ansl=min(ansl,L);} if(l>ans){ans=l;ansl=L;} } return; } int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i)a[i]=read(),fj(a[i]); for(int i=1;i<=maxx;++i)if(p[i])solve(i); if(!ansl)printf("%d %d\n",ansl,ansl); else printf("%d %d\n",ansl,ansl+ans-1); return 0; }
但是还需要考虑一波事情 叫做二分过题 。优化这个二分如何?我们还是把1 抽出来进行这个上述二分答案的过程发现这样的复杂度也是科学的n*8*logn 哇很稳。当时考试的时候关键是少了一个推论。
很可惜。我应该是能够推出那个答案所在的推论的,要不就A了。
A了都不知道 以为wa了 那就很棒了 我的思路也是正确的二分 +验证 不过把1抽出来罢了 跟上述做法差多了 但是没有上述做法好调。
但是要注意细节 二分果然毒瘤。...
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned 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 ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=500010,maxn=1000010; int n,flag,maxx=1000000,mark; int ans,ansl,top; int a[MAXN],w[MAXN]; int b[MAXN],c[MAXN],f[MAXN]; vector<int>p[maxn],id[maxn]; inline void upd(int L,int len) { if(len>ans)ansl=L,ans=len; else if(len==ans)ansl=min(ansl,L); return; } inline int check(int x) { int minn=INF,s; for(int i=1;i<=x;++i)f[i]=(c[i]<<1)-b[i]; for(int i=x+1;i<=top;++i) { f[i]=(c[i]<<1)-b[i]; if(f[i-x]<minn){minn=f[i-x];s=i-x;} if(f[i]-minn+1>=0) { int len=2*(c[i]-c[s]+1); int L=max(1,b[i]-len+1); upd(L,len); return 1; } } return 0; } int main() { //freopen("1.in","r",stdin); n=read();mark=n/2;if(n&1)++mark; for(int i=2;i<=maxx;++i) if(!p[i].size()) for(int j=i;j<=maxx;j+=i) p[j].push_back(i); for(int i=1;i<=n;++i) { a[i]=read(); if(a[i]!=1)flag=1; for(un int j=0;j<p[a[i]].size();++j) { id[p[a[i]][j]].push_back(i); //cout<<p[a[i]][j]<<endl; if(id[p[a[i]][j]].size()>=mark)mark=INF; } } if(!flag){printf("0 0\n");return 0;} if(mark==INF){printf("1 %d\n",n);return 0;} for(int i=2;i<=maxx;++i) { if(!id[i].size())continue; top=0; for(unsigned int j=0;j<id[i].size();++j) { b[++top]=id[i][j]; c[top]=j+1; if((c[top]<<1)>=b[top])upd(max(1,b[top]-c[top]*2+1),c[top]<<1); } int l=1,r=top; while(l+1<r) { int mid=(l+r)>>1; if(check(mid))l=mid; else r=mid; } check(r);check(l); } printf("%d %d\n",ansl,ansl+ans-1); return 0; }
T1:由于本人智商不够 耐心不够 不够沉着 不够冷静 不够勇敢 不够自信 总之就是不对劲这人 60分送的都没拿 我我我真不对劲。
LINK:模拟 构造 毒瘤 要求多 不会写 自闭。
具体要求 要求构造一个字符串 其由 小写字母 ?* 组成 这个类似于通配符(一听到这个名字我就烦... *可以替换任意非负长度的小写字母 注意非负也可以是0 ?可以替换任意小写字母。
给出两个 字符串S1 S2 要求出一个字符串T可以满足:如果字符串S能匹配S1或S2 那么S也能匹配T 这个条件很绕对不对。和S明明无关 非得引入S。
显然有结论 如果S能匹配S1 S1能匹配T 那么显然 T能匹配S 那么我们让T能够同时匹配S1和S2 即可。且使得*尽量少 且在满足上述条件下长度最长 且在满足上述条件之下?个数最少。
且在满足上述条件的情况下字典序最小 其中字典序的大小关系为*?abcd... 真的不想写TAT.
(别问我为什么写 我只是突然睡着了 睡醒之后就想写了。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned 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 ll read() { ll 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=200010; int T,n,m; char a[MAXN],b[MAXN]; int main() { //freopen("1.in","r",stdin); T=read(); while(T--) { scanf("%s",a+1); scanf("%s",b+1); n=strlen(a+1);m=strlen(b+1); if(n==m) { for(int i=1;i<=n;++i) { if(a[i]=='?'||b[i]=='?'){printf("?");continue;} if(a[i]!=b[i]){printf("?");continue;} printf("%c",a[i]); } puts(""); } } return 0; }
这题 显然两个性质 一个 *最多一个 另一个 *带????必然可能是最多答案这也意味着答案的长度被固定了固定在min(n1,m1)两个串不含*号的长度上。
考虑接下来如何的构造 发现前面尽可能的匹配 后面也尽可能的匹配匹配不上就是?号也就是说要让?最少了。考虑枚举*号的位置从而确定?的多少。
那么我们正着做一遍匹配倒着做一遍匹配 这样子我们直接枚举i点为* 取最少的?即可。考虑字典序 我们发现在保证?最少之前是没有字典序的限制的。
所以 而?的字典序也是如此 *越靠前越优所以我们去>即可。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned 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 ll read() { ll 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=200010; int T,n,m; int n1,m1; char a[MAXN],b[MAXN]; char ansl[MAXN],ansr[MAXN]; int cntl[MAXN],cntr[MAXN]; int main() { freopen("1.in","r",stdin); T=read(); while(T--) { scanf("%s",a+1);scanf("%s",b+1); n=strlen(a+1);m=strlen(b+1); n1=0;m1=0; memset(cntl,0,sizeof(cntl)); memset(cntr,0,sizeof(cntr)); for(int i=1;i<=n;++i)if(a[i]!='*')++n1; for(int i=1;i<=m;++i)if(b[i]!='*')++m1; if(n==m&&n1==n&&m1==m) { for(int i=1;i<=n;++i) { if(a[i]=='?'||b[i]=='?'){printf("?");continue;} if(a[i]!=b[i]){printf("?");continue;} printf("%c",a[i]); } puts("");continue; } int len=min(n1,m1)+1;//答案串的总长度 int flag=0; for(int i=1;i<=len;++i) { if(flag||a[i]=='*'||b[i]=='*') { flag=1;cntl[i]=cntl[i-1]+1; ansl[i]='?';continue; } else { if(a[i]==b[i]&&a[i]!='?')cntl[i]=cntl[i-1],ansl[i]=a[i]; else cntl[i]=cntl[i-1]+1,ansl[i]='?'; } } flag=0; for(int i=len;i;--i) { int s1=n-(len-i),s2=m-(len-i); if(flag||a[s1]=='*'||b[s2]=='*') { flag=1;cntr[i]=cntr[i+1]+1; ansr[i]='?';continue; } else { if(a[s1]==b[s2]&&a[s1]!='?')cntr[i]=cntr[i+1],ansr[i]=a[s1]; else cntr[i]=cntr[i+1]+1,ansr[i]='?'; } } int ans=INF,pos; for(int i=1;i<=len;++i) if(cntl[i-1]+cntr[i+1]<ans) { ans=cntl[i-1]+cntr[i+1]; pos=i; } for(int i=1;i<pos;++i)printf("%c",ansl[i]); printf("*"); for(int i=pos+1;i<=len;++i)printf("%c",ansr[i]); puts(""); } return 0; }
T3:这题还是 一个比较有意思的题目 不过我只会暴力(不推出一些性质 那么暴力也就不可能出奇迹吧。所以看题还得推出一些性质 不要不想思考。要从题目中的信息之中不断的发掘性质。
LINK:黑 需要掉命的题目必须推出性质才能解题,鬼畜的dp。
题目大意是 给出了一段程序 求出一个排列是的最后的Y值最大。还要输出构造的排列。
开始缓慢入题 要求不妨把所有的排列都搜出来判断 显然这样是n! 乌拉然后你会发现 什么也得不到。考虑装压合并状态 f[i][j]表示 状态i且x为j的答案最大。
这玩意转移是2^n*n*x的终于得到了30分的好成绩。然后弃疗即可/cy
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #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=2010; int n,X,t,w,v,s,maxx; int a[MAXN],b[MAXN]; int q[MAXN]; int f[1<<16][105]; int g[1<<16][105],h[1<<16][105]; inline void get_path(int state,int k) { if(!state)return; q[++t]=g[state][k]; get_path(state^(1<<(q[t]-1)),h[state][k]); } int main() { //freopen("1.in","r",stdin); X=read();n=read(); for(int i=1;i<=n;++i)a[i]=read(); for(int i=1;i<=n;++i)b[i]=read(); if(n<=15) { memset(f,0xcf,sizeof(f)); f[0][X]=0; for(int i=0;i<(1<<n);++i) { for(int j=0;j<=X;++j) { if(f[i][j]<0)continue; for(int k=1;k<=n;++k) { if(i&(1<<(k-1)))continue; s=i|(1<<(k-1)); w=max(0,j-a[k]); v=max(0,b[k]-w); if(f[s][w]<f[i][j]+v) { f[s][w]=f[i][j]+v; g[s][w]=k;h[s][w]=j; } } } } for(int i=0;i<=X;++i) maxx=max(maxx,f[(1<<n)-1][i]); for(int i=0;i<=X;++i) if(maxx==f[(1<<n)-1][i]) { get_path((1<<n)-1,i); break; } printf("%d\n",maxx); //cout<<t<<endl; printf("%d",q[t]); for(int i=t-1;i>=1;--i)printf(" %d",q[i]); return 0; } }
obviously 发现一个解 或者说最优解必然是存在在前几步累计的价值都为0 也就是我们研究的阶段变成了两步一步是爆零阶段 一步是非爆零阶段 x一旦爆零那么 那么我也跟着爆零/cy
当然不是一旦爆零后面的顺序也就无所谓了 因为对答案贡献都是其累和 也就是说 我们只需要研究前面的阶段即可。 考虑dp。
如何让x以某种方式完成优美的爆零使我们的目标 这个时候 就考验我们 的爆零能力了。发现x在非爆零阶段 承受的x都是递增的。
obvious 因为在非爆零的阶段 他们带给答案的累加都是 0 那么先搞 大的这样到最后更容易获得一些值