還算比較正常 不過犯了一個非常嚴重的問題 文件名寫錯了 我真的檢查了半個小時 硬是沒看出來,我可能太疲倦了當時。
今天得分 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\n",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\n",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\n",ans); return 0; } if(n<=30) { ans=0; dfs(1,maxx,maxx); printf("%lld\n",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\n",ans); return 0; }
(這可能也是最快的代碼了 hash常數極小 借用結構體實現這個操作加上滾動.
當然這並不是正解...然而我不會正解 RT。