吉司機線段樹
假設對於要讓區間[l,r]對於x做與操作
對於區間[l,r]維護他們的區間或(用qh表示)
分三種情況討論
1.當qh|x=qh時,顯然這一次修改不會造成影響,直接退出
2.當qh|x!=qh 是遞歸左右子樹,直到l=r
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=5e5+10; struct node{ int l,r,qh,ma; }tr[N<<2]; int a[N]; inline int rd() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } void pushup(int u){ tr[u].ma=max(tr[u<<1|1].ma,tr[u<<1].ma); tr[u].qh=(tr[u<<1|1].qh|tr[u<<1].qh); } void build(int u,int l,int r){ if(l==r) tr[u]={l,r,a[l],a[l]}; else{ tr[u]={l,r}; int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); pushup(u); } } void update(int u,int pos,int w){ if(tr[u].l==pos&& tr[u].r==pos) { tr[u].ma=w; tr[u].qh=w; } else{ int mid = tr[u].l + tr[u].r >> 1; if(pos <= mid) update(u << 1,pos,w); else update(u << 1|1,pos,w); pushup(u); } } int query(int u,int l,int r){ if(tr[u].l > r || tr[u].r < l) return 0; if(tr[u].l >= l && tr[u].r <= r) return tr[u].ma; int mid = tr[u].l + tr[u].r >> 1; return max(query(u << 1,l,r),query(u << 1|1,l,r)); } void modify(int u,int l,int r,int x){ // cout<<tr[u].l<<"_"<<tr[u].r<<endl // if(l==0||r==0)return; if(tr[u].l==tr[u].r){ tr[u].qh&=x; tr[u].ma&=x; return; } if((x&tr[u].qh)==tr[u].qh)return; // cout<<">"; int mid=tr[u].l+tr[u].r>>1; if(l<=mid)modify(u<<1,l,r,x); if(r>mid)modify(u<<1|1,l,r,x); pushup(u); } int main(){ int n=rd(),m=rd(); for(int i=1;i<=n;i++){ a[i]=rd(); } build(1,1,n); while(m--){ char opt[10]; int l,r,x; scanf("%s",opt); if(opt[0]=='A'){ // cout<<"??"; l=rd(),r=rd(),x=rd(); modify(1,l,r,x); } else if(opt[0]=='U'){ l=rd(),x=rd(); update(1,l,x); } else { l=rd(),r=rd(); printf("%d\n",query(1,l,r)); } } }
區間dp板子題
定義dp[i][j]為合並區間i到j獲取的分數,w[i][j]為區間i到j內的樹相乘取模100003的值
轉移方程:dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+(w[i][k]-w[k+1][j])*(w[i][k]-w[k+1][j]));
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1000003; int n,a[400]; ll dp[400][400],w[400][400]; int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; } for(int i=1;i<=n;i++){ ll tmp=1; for(int j=i;j<=n;j++){ tmp=(tmp*a[j])%mod; w[i][j]=tmp; // cout<<i<<" "<<j<<" "<<tmp<<endl; } } for(int len=2;len<=n;len++){ for(int i=1;i+len-1<=n;i++){ int j=i+len-1; for(int k=i;k<j;k++){ dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+(w[i][k]-w[k+1][j])*(w[i][k]-w[k+1][j])); } } } cout<<dp[1][n]<<endl; }
將所有串插入trie樹並建立ac自動機。 表示u結點表示的字符串出現的數量。
表示u在trie樹上的父節點。
表示u在fail樹上的父結點。之后考慮用
表示以結點u對應的字符串為起點的最長字符串嵌套的長度。
可以發現 。直接遍歷trie樹轉移即可。時間復雜度
。建立fail樹后原來的tire樹會發生改變,所以需要復制一個trie2 在遍歷trie2樹時進行狀態轉移
、
#include<bits/stdc++.h> using namespace std; const int N=500010; int fail[N],fa[N],tree[N][26],wend[N],cnt=0; int tree2[N][26]; int dp[N],vis[N],ans=0; inline void build(string s){ int now=0; for(int i=0;i<s.size();i++){ int next=s[i]-'a'; if(!tree[now][next]) tree[now][next]=++cnt,tree2[now][next]=tree[now][next]; now=tree[now][next]; } wend[now]++; } void get_fail(){ queue<int>q; for(int i=0;i<26;i++){ if(tree[0][i]){ fail[tree[0][i]]=0; q.push(tree[0][i]); } } while(!q.empty()){ int u=q.front(); q.pop(); for(int i=0;i<26;i++){ if(tree[u][i]){ fail[tree[u][i]]=tree[fail[u]][i]; q.push(tree[u][i]); } else{ tree[u][i]=tree[fail[u]][i]; // cout<<tree[u][i]<<" "<<u<<"_"<<i<<" fail[u]"<<fail[u]<<endl; } } } } void bfs(){ queue<int>q; q.push(0); dp[0]=0; while(!q.empty()){ int u=q.front(); // vis[u]=1; q.pop(); for(int i=0;i<26;i++){ if(tree2[u][i]){ dp[tree2[u][i]]=max(dp[u],dp[fail[tree2[u][i]]])+wend[tree2[u][i]]; ans=max(ans,dp[tree2[u][i]]); q.push(tree2[u][i]); } } } } int main(){ int n; cin>>n; string s; for(int i=1;i<=n;i++){ cin>>s; build(s); } fail[0]=0; get_fail(); // for(int i=1;i<=9;i++)cout<<i<<"_"<<fa[i]<<endl; bfs(); cout<<ans<<endl; }
歐拉篩板子題
對輸入的n跑一遍歐拉篩,主要是求不同數的貢獻和。如果是1或者素數res+=1,給你的公式3是質因數分解后對第一個質因數及其指數的函數值乘上這個數的其他質因數(包含其指數)。在實現的時候我們發現也可以將公式2、3合並,即tmp值為1或者其他數。
#include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; #define ll long long #define int long long const int maxn = 1e7 + 20; const int inf = 0x3f3f3f3f; const int Base = 131; const ll INF = 1ll << 62; // const double PI = acos(-1); const double eps = 1e-7; const int mod = 1e9 + 7; #define mem(a, b) memset(a, b, sizeof(a)) #define speed \ { \ ios::sync_with_stdio(false); \ cin.tie(0); \ cout.tie(0); \ } inline ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); } inline int rd() { int x; scanf("%lld", &x); return x; } void ex_gcd(int a, int b, int &x, int &y) { // cout <<" a = " << a <<" b = " << b <<" x = " << x <<" y = " << y << endl; if (b == 0){ x = 1; y = 0; return; } ex_gcd(b, a % b, x, y); int tmp = x; x = y; y = tmp - (a / b) * y; } int primes[maxn], vis[maxn] = {0}, cnt = 0; // 歐拉篩板子修改 int Euler(int n){ int res = 1; for(int i = 2; i <= n; i++){ // 如果是素數 if(!vis[i]){ vis[i]=1; primes[cnt++] = i; res++; } for(int j = 0; j < cnt && primes[j] <= n / i; j++){ int tmp = primes[j] * i; vis[tmp] = 1; int count = 0; // 獲取指數大小 while(tmp % primes[j] == 0){ count++; tmp /= primes[j]; } // 通過打表找規律可以發現2、3可以合並寫 res += pow(primes[j], count / 2) * tmp; if(i % primes[j] == 0){ break; } } } return res; } signed main(){ int n = rd(); printf("%lld\n", Euler(n)); system("pause"); return 0; }
思路:使用tarjan計算每個連通塊放子之后能產生的最大連通塊數量,最大連通塊數量等於連接一個割點的橋的數量。注意特判連通塊大小為1的時候的情況。
刪除一個無向圖中的點,能使得原圖增加幾個連通分量?
如果該點是一個孤立的點,那么增加-1個。
如果該點不是割點,那么增加0個。
如果該點是割點且非根節點,那么增加該點在dfs樹中(無反向邊連回早期祖先的)的兒子數。
如果該點是割點且是一個dfs樹的根節點,那么增加該點在dfs樹中(無反向邊連回早期祖先的)的兒子數-1的數目,也就是增加了以該dfs樹的兒子數目-1
#include<bits/stdc++.h> using namespace std; typedef pair<int,int>pii; const int N=4300000; int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; char mp[2010][2010]; int id[2010][2010]; int n,m,ecnt,num,root,top; bool iscut[N]; struct edge{ int v,next; }e[4*N]; int head[N],dfn[N],low[N],cut[N],stack[N],dclock,ins[N]; void init(){ memset(head,-1,sizeof head); ecnt=0; } void addedge(int u,int v){ e[ecnt]={v,head[u]}; head[u]=ecnt++; } void tarjan(int x){ if(x==root)cut[x]--; dfn[x]=low[x]=++dclock; int child=0; for(int i=head[x];~i;i=e[i].next){ int v=e[i].v; if(!dfn[v]){ child++; tarjan(v); low[x]=min(low[x],low[v]); if(low[v]>=dfn[x]){ cut[x]++; if(x!=root||child>1)iscut[x]=1; } } else low[x]=min(low[x],dfn[v]); } } int main(){ init(); cin>>n>>m; for(int i=1;i<=n;i++){ scanf("%s",mp[i]+1); } int cnt=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(mp[i][j]=='.')id[i][j]=++cnt; } } int flag=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(mp[i][j]=='.'&&mp[i][j+1]=='.'){ addedge(id[i][j],id[i][j+1]); addedge(id[i][j+1],id[i][j]); } if(mp[i][j]=='.'&&mp[i+1][j]=='.'){ addedge(id[i][j],id[i+1][j]); addedge(id[i+1][j],id[i][j]); } } } int k=0; for(int i=1;i<=cnt;i++){ if(!dfn[i]){ k++; root=i; tarjan(i); } } if(k==cnt){ cout<<k-1<<endl; return 0; } int res=0; for (int i=1; i<=cnt; i++) if(iscut[i]) res=max(res,cut[i]); cout<<k+res<<endl; }
水題
求怎樣給樣例才會讓題目中給的並查集代碼運行次數超過T,即運行次數最大
#include<bits/stdc++.h> using namespace std; int rd(){ int x; scanf("%d", &x); return x; } vector<int>v; int main(){ int n = rd(); int m = rd(); for(int i = n; i >= 2; i--){ printf("%d %d\n", i, i - 1); } printf("%d %d\n", 1, n); // system("pause"); return 0; }
博弈論:NIM階梯游戲+SG函數,對於一條賽道,連在一起的n個小車可以任選右邊(1——n)輛向右移動一個單位,相當於階梯nim游戲的一層階梯上可以移動(1——n)個小球到下一層
每一輛車,他右邊空格的數量就是他所在的階梯位置
那么這就是一個經典階梯NIM游戲,求奇數層的小球數的異或和,多條賽道同時進行,直接套用SG函數
#include<bits/stdc++.h> using namespace std; typedef pair<int,int>pii; inline int rd(){ int x; scanf("%d",&x); return x; } int main(){ int t=rd(); while(t--){ int n=rd(); vector<int>mex; for(register int i=1;i<=n;i++){ int m=rd(),e=rd(); vector<int>x; for(register int j=1;j<=m;j++){ int tmp=rd(); x.push_back(tmp); } sort(x.begin(),x.end()); vector<pii>pos; int now=0,base=0; for(register int j=0;j<x.size();j++){ if(x[j]+1==x[j+1]){ now++; continue; } int tmp=e-x[j]-(x.size()-j-1); if(tmp%2==1){ base^=(now+1); } now=0; } mex.push_back(base); } int base=0; for(auto tmp:mex){ base^=tmp; } if(base==0){ cout<<"Bob"<<endl; } else cout<<"Alice"<<endl; } }
水題
判斷一下左右兩邊能夠取的機器數量,取min就好了
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1000003; const int maxn=5e5+10; int n,m; int mp[maxn],mp2[maxn]; int main(){ scanf("%d %d",&n,&m); for(int i=1;i<=n;i++)mp[i]=0,mp2[i]=0; int a=n,b=n; for(int i=1;i<=m;i++){ int c,d; scanf("%d %d",&c,&d); mp[c]++; mp2[d]++; if(mp[c]>=n)a--; if(mp2[d]>=n)b--; } printf("%d\n",min(a,b)); return 0; }
水題
最優情況下最后留下的就是和能吃個數一樣的情況,即留下最少的數量
#include<bits/stdc++.h> using namespace std; #define ll long long #define int ll const int M=2e5; int t1[M],t2[M]; int n,m; inline int rd() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } signed main(){ int n = rd(); int m = rd(); int res = 0; int sum = 0; for(int i = 0; i < n; i++){ int x = rd(); sum += x; } if(sum<m)cout<<"0"<<endl; else cout<<sum-(m+1)/2<<endl; return 0; }
首先, 是沒用的,只有
有用
那么問題轉換為, 個物品,當
時才可拿,且拿后
,問最低多少可以拿完全部。
這是一個經典之典中典問題
那么顯然, 的無腦拿,貪心從限制低的到限制高的拿
那么現在考慮拿 的情況,考慮先拿了i就不能拿j,而先拿了j反而可以拿i的情況
V+Ci<Hi
V+Cj>=Hj
Hi-Cj<=v<Hj-ci
Hi+Ci<=Hj+Cj
所以只需要按照Hi+Ci 的值倒序進行貪心就好
發現新加DLC只需要插入到對應位置,這個操作和貪心詢問是可以同時進行的,因此可以 完成
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+10; int h[N]; inline int rd(){ int x; scanf("%lld",&x); return x; } struct bz{ int h,c; }; struct bf{ int h,c; }; bool cmp1(bz a,bz b){ return a.h<b.h; } bool cmp2(bf a,bf b){ return a.h+a.c>b.h+b.c; } vector<bz>cz; vector<bf>cf; inline int getans(int h,int c){ int res=-3e9-10,now=0,flag=0; for(register int i=0;i<cz.size();i++){ if(cz[i].h>=h&&c>0){ res=max(res,h-now); now+=c; bz tmp=cz[i]; cz[i]={h,c}; cz.push_back({0,0}); for(register int j=i+1;j<cz.size();j++){ // cout<<tmp.h<<endl; swap(tmp,cz[j]); res=max(res,cz[j].h-now); now+=cz[j].c; flag=1; } break; } res=max(res,cz[i].h-now); now+=cz[i].c; } if(!flag&&c>0){ cz.push_back({h,c}); res=max(res,h-now); now+=c; } flag=0; for(register int i=0;i<cf.size();i++){ if((cf[i].h+cf[i].c<=h+c)&&c<=0){ bf tmp=cf[i]; cf[i]={h,c}; res=max(res,cf[i].h-now); now+=cf[i].c; cf.push_back({0,0}); for(register int j=i+1;j<cf.size();j++){ swap(tmp,cf[j]); res=max(res,cf[j].h-now); now+=cf[j].c; } flag=1; break; } res=max(res,cf[i].h-now); now+=cf[i].c; } if(!flag&&c<=0){ cf.push_back({h,c}); res=max(res,h-now); now+=c; } return res; } signed main(){ int n=rd(),q; for(int i=1;i<=n;i++){ h[i]=rd(); } for(int i=1;i<=n;i++){ int a=rd(),b=rd(); int c=b-a; if(c>0)cz.push_back({h[i],c}); else cf.push_back({h[i],c}); } sort(cz.begin(),cz.end(),cmp1); sort(cf.begin(),cf.end(),cmp2); int res=-3e9-10,now=0; for(register auto x:cz){ res=(res>x.h-now)?res:x.h-now; now+=x.c; } for(register auto x:cf){ res=(res>x.h-now)?res:x.h-now; now+=x.c; } printf("%lld\n",res); q=rd(); while(q--){ int h=rd(),a=rd(),b=rd(); int c=b-a; printf("%lld\n",getans(h,c)); } } //4 //2 4 5 6 //1 2 //1 2 //1 2 //1 2 //1 //3 1 2