还算比较正常 不过犯了一个非常严重的问题 文件名写错了 我真的检查了半个小时 硬是没看出来,我可能太疲倦了当时。
今天得分 200分有40分因T3 文件名打错挂掉 T1 100分 T2 100分 T3 0分
、
显然发现 外面的括号的排列方式和里面的无关但是只要合法就能对答案进行一定的贡献 考虑并列的括号的贡献即可。括号套括号不防开栈进去统计完里面的贡献再统计外面的 可以发现里面和外面的答案统计无关。
所以 dfs模拟栈的这个过程即可。
//#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 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 ll MAXN=1000010; ll n,top,mark,ans; char a[MAXN]; ll vis[MAXN],s[MAXN],f[MAXN]; inline void dfs() { ll cnt=0; while(1) { if(a[mark+1]=='(') { ++mark; ++cnt; dfs(); } if(a[mark+1]==')') { ++mark; ans+=f[cnt]; return; } if(!vis[mark+1]) { ans+=f[cnt]; return; } } } signed main() { //freopen("1.in","r",stdin); freopen("bracket.in","r",stdin); freopen("bracket.out","w",stdout); scanf("%s",a+1); n=strlen(a+1); for(ll i=1;i<=n;++i)f[i]=f[i-1]+i;//注意开long long for(ll i=1;i<=n;++i) { if(a[i]=='(')s[++top]=i; if(a[i]==')') { if(!top)continue; vis[s[top]]=1; vis[i]=1; --top; } } //for(ll i=1;i<=n;++i)if(vis[i])cout<<i<<endl; for(ll i=1;i<=n;i=mark+1) { if(!vis[i]){mark=i;continue;} mark=i-1; dfs(); } printf("%lld ",ans); return 0; }
看起来很水的样子 其实可以发现直接map就行了但是害怕常数大 所以采用sort发现可以二分了 但是此时注意我们序列单调 对于一个数字 另一个数字一定在领一边左指针不断移动的过程中右指针也不断的移动 发现都是单调移动的所以复杂度可以降到O(n).
该做法在于瓶颈排序 。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<cctype> #include<algorithm> #include<cstdlib> #include<queue> #include<stack> #include<set> #include<bitset> #include<vector> #include<map> #include<deque> #include<utility> #define INF 2000000010 #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define mp make_pair #define ll long long #define db double using namespace std; char *fs,*ft,buf[1<<15]; 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=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=1000010,maxn=10000010; int n,T,flag,m,maxx=10000000; int a[MAXN],f[maxn]; int main() { //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); freopen("check.in","r",stdin); freopen("check.out","w",stdout); T=read(); for(int k=1;k<=T;++k) { if(k!=1)for(int i=1;i<=n;++i)if(a[i]<=maxx)f[a[i]]=0; n=read();m=read();flag=0; for(int i=1;i<=n;++i) { a[i]=read(); if(a[i]<=maxx)f[a[i]]=1; } if(m<=10000000) { for(int i=1;i<=n;++i) { if(a[i]>m)continue; if(f[m-a[i]]){flag=1;break;} } if(flag)puts("1"); else puts("0"); } else { sort(a+1,a+1+n); int j=n,w; for(int i=1;i<=n;++i) { if(a[i]>m)break; w=m-a[i]; while(a[j]>w&&j)--j; if(!j)break; if(a[j]==w){flag=1;break;} } if(flag)puts("1"); else puts("0"); } } return 0; }
考虑 更快的排序方式 基数排序 常数也不算很大 应该接近于sort 然后 我虽然学过但是代码好像很繁杂的样子 也没有实际练习过所以不太会写这种排序方式。
正解是对值域 分块 该做法是 基于排序和开桶统计答案之上的 我们发现上述做法要不是排序 要不是开桶 这种做法就是将排序和开桶联系在了一起。
当然依靠的也是 vector这个强大的容器 (关于其空间复杂度听说是logn的?所以 这个做法不能严格算O(n)?不管了 看起来就是O(n)的即可。
将值域分块 存到vector里这里我们实现了基本的排序 但是每个块中的数字还是乱序的 所以考虑把对于每个块种的数字我们为其寻找答案 显然答案是不超过两块的所以每次都是我们遍历那两个块将其加入到桶中再对当前块种的数字进行统计答案即可 复杂度 3n左右吧。
没怎么看题解 yy了一个比较靠谱的思路基本上常数非常的小 在主机上也都是1s就过了(这个可能是最快的代码了吧 毕竟比题解的常数优秀很多。
//#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) 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=1000010,maxn=5005; int n,m,B=200000,c,ans,T; vector<int>f[maxn]; vector<int>::iterator it; int cnt[200000<<2]; int main() { //freopen("1.in","r",stdin); T=read(); while(T--) { n=read();m=read();ans=0; c=m/B; for(int i=0;i<=c;++i)f[i].clear(); for(int i=1;i<=n;++i) { int x=read(); int y=x/B; f[y].push_back(x); } for(int i=0;i<=c;++i) { int l=(m-(i+1)*B)/B; int r=l+1; int base=l*B; for(it=f[i].begin();it!=f[i].end();it++) cnt[m-*it-base]=1; for(int j=l;j<=r;++j) for(it=f[j].begin();it!=f[j].end();it++) { if(cnt[*it-base])ans=1; if(ans)break; } for(it=f[i].begin();it!=f[i].end();it++)cnt[m-*it-base]=0; if(ans)break; } if(ans)puts("1"); else puts("0"); } return 0; }
一个比较显然的思路是爆搜 考虑到 我们这样做 不管怎么样都得把所有的分组都给搜出来然后判断结果。
这样显然是不优秀的 考虑一个比较显然的思路可能存在状态相同 但方案不同的东西我们将其合并因为后面带给他们的既然状态相同他们都是同一类的东西。
所以考虑 枚举状态 显然 dp f[i][j][k] 表示前i个数第一个集合的状态为j 第二个集合的状态为k 的方案数 所以显然的转移 复杂度n*k*k 不过这个连40分的都很难通过。
爆空间了 所以考虑一下滚动数组这样我们成功拿到了40分。
//#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 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 ll MAXN=62; ll n,maxx=(1<<18)-1; ll minn=1024; ll a[MAXN],ans,flag,mark; ll f[2][1025][1025];//f[i][j]表示... inline void dfs(ll x,ll s1,ll s2) { if(x==n+1) { if(s1==s2) { ++ans; //cout<<s1<<endl; } return; } dfs(x+1,s1&a[x],s2); dfs(x+1,s1,s2&a[x]); } int main() { //freopen("1.in","r",stdin); freopen("add.in","r",stdin); freopen("add.out","w",stdout); n=read(); if(n==1){puts("0");return 0;} for(ll i=1;i<=n;++i) { a[i]=read(); if(a[i]!=a[i-1]&&i-1>=1)mark=1; if(a[i]>minn)flag=1; } if(!mark) { ans=1; for(int i=1;i<=n;++i)ans=ans*2; printf("%lld ",ans-2); return 0; } if(!flag)//复杂度 n*2^10*2^10 { ll u=0; f[u][1023][1023]=1; for(ll i=1;i<=n;++i) { u=u^1; for(ll j=minn-1;j>=0;--j) { for(ll k=minn-1;k>=0;--k) { if(!f[u^1][j][k])continue; f[u][j&a[i]][k]+=f[u^1][j][k]; f[u][j][k&a[i]]+=f[u^1][j][k]; f[u^1][j][k]=0; } } } for(ll j=0;j<minn;++j)ans+=f[u][j][j]; printf("%lld ",ans); return 0; } if(n<=30) { ans=0; dfs(1,maxx,maxx); printf("%lld ",ans); return 0; } return 0; }
考虑 进一步的优化我们发现 这个状态很多是不必要的 因为一些状态一直都没有达到且没有方案数的累加我们白白的枚举且转移是无效的。所以考虑把每一个状态都存起来我们只枚举那些达到的状态。
我利用hash进行状态的枚举 比上述的快了不少但是还是因为空间的原因 爆掉了 只能得到80分的好成绩。(代码好像没了 意会一下即可。
考虑继续优化我们发现 我还是枚举了无用的状态因为我可能枚举的两个状态根本就匹配 白白的浪费了时间 不妨我们存状态的时候把状态进行绑定然后直接枚举这些绑定的状态 这样快速且有效 绑定显然是pair
我们没有一个好的容器储存pair 所以考虑map一下得到id 然后用数组进行转移即可。极致优化:得分100;
//#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 #define mp(x,y) make_pair(x,y) 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 ll MAXN=62,maxn=1<<18; ll n,maxx=(1<<18)-1; ll id,q[2]; ll a[MAXN],ans,flag,mark; struct wy { ll p,q,v; }t[2][maxn]; map<pair<ll,ll>,ll>H; signed main() { //freopen("1.in","r",stdin); freopen("and.in","r",stdin); freopen("and.out","w",stdout); n=read(); if(n==1){puts("0");return 0;} for(ll i=1;i<=n;++i)a[i]=read(); ll u=0; t[u][++q[u]]=(wy){maxx,maxx,1}; for(ll i=1;i<=n;++i) { u=u^1;q[u]=0;H.clear(); for(ll j=1;j<=q[u^1];++j) { ll w1=t[u^1][j].p&a[i]; ll w2=t[u^1][j].q&a[i]; if(H.find(mp(w1,t[u^1][j].q))==H.end()) { H[mp(w1,t[u^1][j].q)]=++q[u]; t[u][q[u]].v=0;t[u][q[u]].q=t[u^1][j].q; t[u][q[u]].p=w1; } if(H.find(mp(t[u^1][j].p,w2))==H.end()) { H[mp(t[u^1][j].p,w2)]=++q[u]; t[u][q[u]].v=0;t[u][q[u]].p=t[u^1][j].p; t[u][q[u]].q=w2; } ll id1=H[mp(w1,t[u^1][j].q)]; ll id2=H[mp(t[u^1][j].p,w2)]; t[u][id1].v+=t[u^1][j].v; t[u][id2].v+=t[u^1][j].v; } } for(ll j=1;j<=q[u];++j) if(t[u][j].p==t[u][j].q)ans+=t[u][j].v; printf("%lld ",ans); return 0; }
(这可能也是最快的代码了 hash常数极小 借用结构体实现这个操作加上滚动.
当然这并不是正解...然而我不会正解 RT。