(7題弟弟。C題知道正解,懶得寫了)
A:^&^ HDU - 6702
題意:給出A,B。求一個最小的C,使得min=(A^C)&(B^C)最小。
思路:如果存在A和B都有的位,那么全選,就行了,這時結果min為0; 否則,選最小的那個,一個有,一個沒有的那一位p,結果min=1<<p;

#include<bits/stdc++.h> #define ll long long using namespace std; int T; unsigned int A,B,C; template<class T> inline void read(T&a){ char c=getchar(); for(a=0;(c<'0'||c>'9')&&c!='-';c=getchar()); bool f=0;if(c=='-')f=1,c=getchar(); for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0'; if(f)a=-a; } int main(){ for(read(T);T--;){ read(A),read(B);C=0; for(long long i=1;i<=A&&i<=B;i<<=1) if((i&A)&&(i&B))C|=i; if(!C){ for(long long i=1;i<=A||i<=B;i<<=1) if((i&A)||(i&B)){C|=i;break;} } printf("%u\n",C); } return 0; }
B:array HDU - 6703
題意:有兩種操作,第一種單點修改。 第二種,給出(R,K),查詢大於等於K的最小數,滿足在a[1,R]中沒有出現。 強制在線。
思路:記錄每個數出現的最小位置,把它們插入線段樹。那么就是在[K,N]中找第一個對應值>R的,所以線段樹保存的信息的區間最大值。 這個由於線段樹自帶二分功能,即如果作區間有Mx>R的,那么在左區間找答案,是否在右區間找; set保存相同數的最小位置; 所以整體的復雜度就是O(NlogN);

#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=400010; int a[maxn],Mx[maxn],RR; set<int>s[maxn]; set<int>::iterator it; void read(int &x){ x=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } void update(int Now,int L,int R,int pos,int val) { if(L==R){ Mx[Now]=val; return ; } int Mid=(L+R)>>1; if(pos<=Mid) update(Now<<1,L,Mid,pos,val); else update(Now<<1|1,Mid+1,R,pos,val); Mx[Now]=max(Mx[Now<<1],Mx[Now<<1|1]); } int query(int Now,int L,int R,int l,int r,int RR) { int Mid=(L+R)>>1,res=0; if(Mx[Now]<=RR) return 0; if(l<=L&&r>=R) { if(L==R&&Mx[Now]>RR) return L; if(Mx[Now<<1]>RR) return query(Now<<1,L,Mid,l,r,RR); if(Mx[Now<<1|1]>RR) return query(Now<<1|1,Mid+1,R,l,r,RR); return 0; } if(l<=Mid) res=max(res,query(Now<<1,L,Mid,l,r,RR)); if(res) return res; if(r>Mid) res=max(res,query(Now<<1|1,Mid+1,R,l,r,RR)); return res; } int main() { int T,N,M,ans,opt,K,pos; scanf("%d",&T); while(T--){ ans=0; scanf("%d%d",&N,&M); rep(i,1,N) s[i].clear(); rep(i,1,N<<2) Mx[i]=0; rep(i,1,N) read(a[i]); rep(i,1,N) s[a[i]].insert(i); rep(i,1,N){ if(s[i].empty()) continue; int t=*s[i].begin(); update(1,1,N,i,t); } rep(i,1,M){ read(opt); if(opt==1){ read(pos); pos^=ans; if(a[pos]<=N) { it=s[a[pos]].lower_bound(pos); if(it==s[a[pos]].begin()){ s[a[pos]].erase(it); int tmp=N+1; if(!s[a[pos]].empty()) tmp=*s[a[pos]].begin(); if(tmp==N+1) update(1,1,N,a[pos],N+1); else update(1,1,N,a[pos],tmp); } else s[a[pos]].erase(it); a[pos]+=10000000; } } else { read(RR); read(K); RR^=ans; K^=ans; ans=query(1,1,N,K,N,RR); if(ans==0) ans=N+1; printf("%d\n",ans); } } } return 0; }
C:K-th occurrenceHDU - 6704
題意:給定字符串S,Q次詢問,每次給出(L,R,K),讓你求S中 str[L,R]這個子串第K次出現的位置。
思路:顯然是SA的區間問題,可以RMQ出一個區間,滿足這個區間的min(ht)>=R-L+1;然后在這個區間主席樹求第K大。
代碼,稍等兩天。
update-8-30 : 作為一個sam的真粉,最終還是用sam把這題補了。
前置知識點:1,線段樹的啟發式合並,維護每個節點的endpos。 2,倍增可以快速地轉移到某個子串在sam中的位置。
3,線段樹可以二分第K個數。
補了兩個CF的題,可以對照一下:
CodeForces - 666E: Forensic Examination (SAM 線段樹合並)
CodeForces - 1037H: Security(SAM+線段樹合並)

#include<bits/stdc++.h> #define rep2(i,a,b) for(int i=a;i>=b;i--) #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; char c[maxn]; int rt[maxn],pos[maxn],N,Q,tot,f[maxn][20]; struct in{ int L,R,sum; }s[maxn*90]; void ins(int &now,int L,int R,int p) { if(!now) now=++tot; s[now].sum++; if(L==R) return ; int Mid=(L+R)>>1; if(p<=Mid) ins(s[now].L,L,Mid,p); else ins(s[now].R,Mid+1,R,p); } void merge(int &now,int x,int y,int L,int R) { if(!x||!y) { now= x|y; return ;} now=++tot; if(L==R) { s[now].sum=s[x].sum+s[y].sum; return ; } int Mid=(L+R)>>1; merge(s[now].L,s[x].L,s[y].L,L,Mid); merge(s[now].R,s[x].R,s[y].R,Mid+1,R); s[now].sum=s[s[now].L].sum+s[s[now].R].sum; } int query(int Now,int L,int R,int K) { if(s[Now].sum<K) return -1; if(L==R) return L; int Mid=(L+R)>>1; if(s[s[Now].L].sum>=K) return query(s[Now].L,L,Mid,K); return query(s[Now].R,Mid+1,R,K-s[s[Now].L].sum); } struct SAM{ int ch[maxn][26],fa[maxn],maxlen[maxn],cnt,last; void init() { rep(i,1,tot) s[i].L=s[i].R=s[i].sum=0; rep(i,0,cnt) G[i].clear(),rt[i]=0; cnt=last=1; tot=0; memset(ch[1],0,sizeof(ch[1])); } void add(int x,int id) { int np=++cnt,p=last; last=np; ins(rt[np],1,N,id); maxlen[np]=maxlen[p]+1; memset(ch[np],0,sizeof(ch[np])); while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p]; if(!p) fa[np]=1; else { int q=ch[p][x]; if(maxlen[q]==maxlen[p]+1) fa[np]=q; else { int nq=++cnt; maxlen[nq]=maxlen[p]+1; fa[nq]=fa[q]; fa[q]=fa[np]=nq; memcpy(ch[nq],ch[q],sizeof(ch[q])); while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p]; } } } vector<int>G[maxn]; void dfs(int u) { for(int i=0;i<G[u].size();i++){ int v=G[u][i]; f[v][0]=u; dfs(v); merge(rt[u],rt[u],rt[v],1,N); } } void DFS() { rep(i,2,cnt) G[fa[i]].push_back(i); dfs(1); rep(i,1,18) rep(j,1,cnt) f[j][i]=f[f[j][i-1]][i-1]; } }T; void solve() { int L,R,K,len; scanf("%d%d%d",&L,&R,&K); len=R-L+1; int now=pos[R]; for(int i=18;i>=0;i--) if(T.maxlen[f[now][i]]>=len) now=f[now][i]; int ans=query(rt[now],1,N,K); if(ans!=-1) ans=ans-len+1; printf("%d\n",ans); } int main() { int Case; scanf("%d",&Case); while(Case--){ T.init(); scanf("%d%d",&N,&Q); scanf("%s",c+1); rep(i,1,N) T.add(c[i]-'a',i),pos[i]=T.last; T.DFS(); while(Q--) solve(); } return 0; }
D:path HDU - 6705
題意:給定有向圖,然后對走路沒有限制,包括起點,終點,是否重復走,都是自由的。 求第K長路徑。 K<=5e4;(內存比較小)
思路:顯然是個比較水的BFS,可以用優先隊列來搞,但是內存不夠。所以用multiset,這樣如果set.size()>K,可以刪除尾巴。 看似O(NlogN)的復雜度,卻卡了。
是這樣的,如果菊花圖,邊比較多,那么復雜度就接近(KM)了,顯然GG,那么我們可以把后續節點按照邊權排序,然后每次取前面幾個小的即可。
(即是set里面可以刪去尾巴,圖里面的G[]的尾巴也可以刪一部分。不然很容易GG。

#include<bits/stdc++.h> #define f first #define s second #define ll long long #define pii pair<ll,int> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=250010; int Mx,tot,N,fcy[maxn]; ll ans[maxn]; vector<pair<int,int> >G[maxn]; void read(int &x){ x=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } void SPFA() { multiset<pii>q; tot=0;ll FCY=0;int SZ=0; multiset<pii>::iterator it; rep(i,1,N) { for(int j=0;j<G[i].size();j++){ q.insert(make_pair(G[i][j].first,G[i][j].second)); SZ++; if(SZ>Mx) q.erase(--q.end()),SZ--; } } while(SZ){ pii now=*q.begin(); int u=now.second; q.erase(q.begin()); SZ--; ans[++tot]=now.first; if(tot>=Mx) return ; for(int i=0;i<G[u].size();i++){ if(SZ&&SZ+tot==Mx&&now.f+G[u][i].f>(*(--q.end())).f) break; int v=G[u][i].second; q.insert(pii{now.first+G[u][i].first,v}); SZ++; while(SZ>Mx-tot) q.erase(--q.end()),SZ--; } } } int main() { int T,M,Q,u,v,w; scanf("%d",&T); while(T--){ scanf("%d%d%d",&N,&M,&Q); rep(i,1,N) G[i].clear(); Mx=0; rep(i,1,M){ read(u); read(v); read(w); G[u].push_back(make_pair(w,v)); } rep(i,1,N) sort(G[i].begin(),G[i].end()); rep(i,1,Q) { read(fcy[i]); Mx=max(Mx,fcy[i]); } SPFA(); rep(i,1,Q) printf("%lld\n",ans[fcy[i]]); } return 0; }
E: huntian oy HDU - 6706
題意:求題意中的互質對數量。
思路:反演一下。

#include <bits/stdc++.h> #include<tr1/unordered_map> #define Accepted 0 using namespace std; typedef long long ll; const int MOD = 1e9 + 7; const int maxn = 5e6 + 10; bool not_prime[maxn]; int prime[maxn / 10]; ll phi[maxn]; void init() { int n = 5000000; int tot = 0; not_prime[1] = 1; phi[1] = 1; for(int i = 2; i <= n; i++) { if(!not_prime[i])prime[tot++] = i, phi[i] = i - 1; for(int j = 0; j < tot && 1LL * prime[j] * i <= n; j++) { not_prime[prime[j] * i] = 1; if(i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; } else { phi[i * prime[j]] = phi[i] * (prime[j] - 1); } } } phi[1] = 1; for(ll i = 2; i <= n; i++) phi[i] = (i * phi[i] % MOD + phi[i - 1]) % MOD; } tr1::unordered_map<ll, ll>sumphi; const ll inv2 = 500000004; const ll inv6 = 166666668; ll Sum(ll x) { if(x <= 5000000)return phi[x]; if(sumphi[x])return sumphi[x]; ll ans = x * (x + 1) % MOD * (2 * x + 1) % MOD * inv6 % MOD; for(ll l = 2; l <= x; l++) { ll r = x / (x / l); ll tmp = (l + r) * (r - l + 1) % MOD * inv2 % MOD * Sum(x / l) % MOD; ans = (ans + MOD - tmp) % MOD; l = r; } return sumphi[x] = ans; } int main() { init(); int T; scanf("%d", &T); while(T--) { ll n, a, b, ans = 0; scanf("%lld%lld%lld", &n, &a, &b); printf("%lld\n", (Sum(n) + MOD - 1) * inv2 % MOD); } return Accepted; }
F:Shuffle Card HDU - 6707
題意:給定N個數的排列,然后M次操作,每次把給定的數放到排列的最前面。求最后的排列。
思路:如果一個數多次放到前面,那么最后一次是有影響的操作。 所以我們倒敘操作,維護一個queue,如果是第一次操作,就處理,放到隊尾。然后把沒有操作過的數按原來順序插入即可。

#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; int Laxt[maxn],Next[maxn],To[maxn],cnt; void add(int u,int v) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } int a[maxn],vis[maxn],p[maxn],ans[maxn],tot; int main() { int T,N,M; scanf("%d%d",&N,&M); rep(i,1,N) scanf("%d",&a[i]); rep(i,1,M) scanf("%d",&p[i]); for(int i=M;i>=1;i--){ if(vis[p[i]]) continue; ans[++tot]=p[i]; vis[p[i]]=1; } rep(i,1,N) if(!vis[a[i]]) ans[++tot]=a[i]; rep(i,1,N) printf("%d ",ans[i]); return 0; }
G: Windows Of CCPC HDU - 6708
簽到題。

#include <bits/stdc++.h> #define Accepted 0 using namespace std; typedef long long ll; const int maxn = 1e5 + 10; bool s[1030][1030]; void dfs(int x, int y, int d, bool f) { if(d == 2) { if(f) { s[x][y] = s[x][y + 1] = s[x + 1][y + 1] = 1; s[x + 1][y] = 0; } else { s[x][y] = s[x][y + 1] = s[x + 1][y + 1] = 0; s[x + 1][y] = 1; } } else { d /= 2; dfs(x, y, d, f); dfs(x, y + d, d, f); dfs(x + d, y, d, !f); dfs(x + d, y + d, d, f); } } int main() { int T, n; scanf("%d", &T); while(T--) { scanf("%d", &n); dfs(0, 0, (1 << n), 1); for(int i = 0; i < (1 << n); i++) { for(int j = 0; j < (1 << n); j++) if(s[i][j])putchar('C'); else putchar('P'); putchar('\n'); } } return Accepted; }
H: Fishing Master HDU - 6709
題意:有一個鍋,一個魚竿,釣魚的時間的固定的K,每個魚煮的時間是a[]。 同時最多釣一個魚,同時最多煮一個魚,問最短的時間,把魚都釣起來煮好。
思路:按時間倒敘排序。 然后處理就行了。 但是做着會發現,這樣顯然還不夠。 我們需要想辦法把a[]%K之后在操作。

#include<bits/stdc++.h> #define ll long long using namespace std; int T,n,k,t[100010],cnt; long long ans; template<class T> inline void read(T&a){ char c=getchar(); for(a=0;(c<'0'||c>'9')&&c!='-';c=getchar()); bool f=0;if(c=='-')f=1,c=getchar(); for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0'; if(f)a=-a; } int main(){ for(read(T);T--;){ ans=cnt=0;read(n);read(k); for(register int i=1;i<=n;i++){ read(t[i]),ans+=t[i]+k; cnt+=t[i]/k;t[i]%=k; } ans-=(long long)k*min(cnt,n-1); int j=n-1-min(cnt,n-1); sort(t+1,t+1+n); for(int i=n;i>=n-j+1;i--)ans-=t[i]; printf("%lld\n",ans); } return 0; }