模擬掛成\(10\)分??ios
每次更新答案的時候位置搞錯了。算法
想到了多是線段樹動態開點,但沒寫出來,由於標記下傳不會。。。數組
理解錯了題目含義。學習
選出的\(m\)個物品中,至少要有\(k\)個是\(A\)喜歡的,至少\(k\)個是\(B\)喜歡的優化
那麼很顯然只要知足了上面的限制條件,倆人都不喜歡的也能選。。。spa
但考場上沒想到這層code
就涼了blog
正解變騙分,\(15\)分排序
搞完上面兩個題目以後沒剩多少時間,就隨便扔了個東西上去。。圖片
也不知道寫的是個啥
/* * 第一眼感受是線段樹 * 想了想,感受線段樹太浪費了,bitset應該能夠搞 * 可是。。。看了數據以後 * 長度爲10^18的01序列 * 這。。作個頭啊作。 * * 第三種操做是0變1,1變0 * * 對於n,m<=1000的數據,能夠模擬 * 對於只有1操做的狀況,考慮維護全0區間和全1區間的分界點 * 對於只有1,2操做的狀況,由於是強制變成0或1,因此一樣只考慮分界點 * 對於n<=10^5的狀況,線段樹應該能夠?可是不會維護。 */ #include <cstdio> #include <algorithm> inline long long read() { long long n=0;int w=1;register char c=getchar(); while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0' && c<='9')n=n*10+c-'0',c=getchar(); return n*w; } const int M=1e5+1,qwq[]={0,0,1}; int a[100001],ty[M]; long long l[M],r[M]; std::pair<int,int> pr[M]; int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int m,flag=0;long long n=0; m=read(); for(int i=1;i<=m;++i) { ty[i]=read(),l[i]=read(),r[i]=read(); n=std::max(n,r[i]); flag=std::max(flag,ty[i]); } if(flag==1) { int id=1,cnt=0; for(int i=1;i<=m;++i) { if(l[i]<=id && r[i]>=id) { int x=r[i]+1; for(int j=1;j<i;++j) if(l[j]<=x && r[j]>=x) x=r[j]+1; id=x; } printf("%d\n",id); } } else if(n<=100000) { int id=1; for(int x,i=1;i<=m;++i) { if(ty[i]!=3) { x=(ty[i]==1?1:0); for(int j=l[i];j<=r[i];++j) a[j]=x; } else for(int j=l[i];j<=r[i];++j) a[j]^=1; if(l[i]<=id && id<=r[i]) { for(;l[i]<=r[i];++l[i]) if(!a[l[i]]) { id=l[i]; break; } } printf("%d\n",id); } } else while(m--) puts("1"); fclose(stdin),fclose(stdout); return 0; }
/* * 分紅兩人都喜歡的,只有A喜歡的,只有B喜歡的 * 而後枚舉從兩我的都喜歡的物品裏面選k個最便宜的 * 而後貪心選剩下的部分 * * 大樣例跑不出來。。 */ #include <cstdio> #include <algorithm> inline int read() { int n=0,w=1;register char c=getchar(); while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9')n=n*10+c-'0',c=getchar(); return n*w; } const int N=2e5+1; int n,m,k,a[N],b[N],v[N],both[N]; /* int aaa,bbb; void dfs(int now,int step,int aa,int bb) { if(aa>k || bb>k)return ; if(step==m) { if(aa==k && bb==k) ++ans; return ; } if(aaa==now) { ++aaa; if(bbb==now) { ++bbb; dfs(now+1,step+1,aa+1,bb+1); dfs(now+1,step,aa,bb); --bbb; } else { dfs(now+1,step+1,aa+1,bb); dfs(now+1,step,aa,bb); } --aaa; } else if(bb==now) { ++bbb; dfs(now+1,step+1,aa,bb+1); dfs(now+1,step,aa,bb); --bbb; } } */ inline bool cmp(const int &x,const int &y) {return v[x]<v[y];} int main() { freopen("b.in","r",stdin); freopen("b.out","w",stdout); n=read(),m=read(),k=read(); for(int i=1;i<=n;++i) v[i]=read(); int aa=read(),bb; for(int i=1;i<=aa;++i) a[i]=read(); bb=read(); for(int i=1;i<=bb;++i) b[i]=read(); std::sort(a+1,a+1+aa); std::sort(b+1,b+1+bb); //哪些倆人都喜歡 int aaa=1,bbb=1,emp=0; while(aaa<=aa && bbb<=bb) { if(a[aaa]>b[bbb]) ++bbb; else if(a[aaa]<b[bbb]) ++aaa; else { both[++emp]=a[aaa]; ++aaa,++bbb; } } /* for(int i=1;i<=emp;++i) printf("%d ",both[i]); */ // std::sort(both+1,both+1+emp,cmp); long long ans=0; for(int i=1;i<=k;++i) ans+=v[both[i]]; if(emp>k) {//先把都喜歡的減掉 m-=k; k=0; } else { m-=emp; k-=emp; } //判斷剩下的是否是夠 if(m<(k<<1)) { printf("-1"); fclose(stdin);fclose(stdout); return 0; } int cnt=1;emp=0; for(int i=1;i<=aa;++i) { if(a[i]!=both[cnt]) a[++emp]=a[i]; else ++cnt; } aa=emp; cnt=1,emp=0; for(int i=1;i<=bb;++i) { if(b[i]!=both[cnt]) b[++emp]=b[i]; else ++cnt; } bb=emp; if(k>aa || k>bb) { printf("-1"); fclose(stdin);fclose(stdout); return 0; } /* for(int i=1;i<=aa;++i) printf("%d ",a[i]); puts(""); for(int i=1;i<=bb;++i) printf("%d ",b[i]); */ std::sort(a+1,a+1+aa,cmp); std::sort(b+1,b+1+bb,cmp); //而後貪心選剩下的 for(int i=1;i<=k;++i) ans+=v[a[i]]+v[b[i]]; int i=k+1,j=k+1; while(i+j<m) { if(v[a[i]]>v[b[j]]) ans+=v[b[j++]]; else ans+=v[a[i++]]; } printf("%lld",ans); fclose(stdin);fclose(stdout); return 0; }
#include<cstdio> #include<iostream> #include<fstream> #include<cmath> #include<cstring> #include<algorithm> using namespace std; #define Set(a) memset(a,0,sizeof(a)) #define F(i,a,b) for(int i=a;i<=b;++i) #define UF(i,a,b) for(int i=a;i>=b;--i) #define openf(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout) typedef long long LL; typedef long long ll; typedef unsigned long long ULL; typedef unsigned long long ull; const int maxn=400+10; const int maxm=5e4+10; ll n,m; ll u[maxm],v[maxm]; bool used[maxn],a[maxn][maxn]; ll f[maxn],xx; ll tt; void check() { xx=0; Set(f); F(i,1,n) if(!used[i]) f[++xx]=i; F(i,1,xx) F(j,1,i) if(f[i]!=f[j]&&!a[f[i]][f[j]]) {a[f[i]][f[j]]=1;++tt;} } void dfs(int step) { if(step==m+1) check(); ll u1=u[step],v1=v[step]; if(!used[u1]&&!used[v1]){used[u1]=1;dfs(step+1);used[u1]=0;used[v1]=1;dfs(step+1);used[v1]=0;} if( used[u1]&&!used[v1]){used[v1]=1;dfs(step+1);used[v1]=0;} if(!used[u1]&& used[v1]){used[u1]=1;dfs(step+1);used[u1]=0;} if( used[u1]&& used[v1])dfs(step+1); return; } int main() { openf(c); scanf("%d%d",&n,&m); F(i,1,m) scanf("%d%d",&u[i],&v[i]); Set(used); Set(a); dfs(1); printf("%lld",tt); return 0; }
離散化線段樹
對每一個區間,維護一個\(tag\),維護的是最左邊的\(0\)和最左邊的\(1\)的位置
那麼\(1\)操做就是把這個區間的\(tag\)變成區間左端點
而後考慮標記下傳
\(1\)操做\(+\)任意操做仍是\(1\)操做
\(2\)操做\(+\)任意操做仍是\(2\)操做
\(3\)操做\(+1\)操做變成\(2\)操做
\(3\)操做\(+2\)操做變成\(1\)操做
\(3\)操做\(+3\)操做變成沒有操做
而後直接離散化+線段樹就好了,不須要動態開點
把全部的物品分紅:兩人都喜歡的,\(A\)喜歡的,\(B\)喜歡的,都不喜歡的
枚舉兩人都喜歡的物品選了多少個(假設是\(x\)),那麼再在\(A\)喜歡的物品中和\(B\)喜歡的點鐘至少都還要選\(k-x\)個(用貪心的思路想,這裏顯然要選權值前\(k-x\)小的那些),而後若是仍是沒有選夠\(m\)個,就在兩人都不喜歡的物品中選最小的那些直到知足條件
那麼從小到大枚舉\(x\),不停求和就好了
對這個問題,考慮一個更通常的問題:假設\(f_k(S)=f_{k-1}(S\cup \{v_i\})\)表示\(k\)我的來過以後,\(S\)集合內的蘋果都還存活的機率是否\(\gt 0\),討論獲得三種狀況
那麼如今將全部的\(f_m(\{p\})=1\)都求出來,順便求出\(g(p)\)是從\(f_m(\{p\})\)運行上面算法獲得的最終集合
觀察獲得\((u,v)\)合法的條件就是\(f_m(\{u\})=1,f_m(\{v\})=1,g(u)\cap g(v)=\varnothing\)
直接枚舉,便可獲得答案
給定一棵\(n\)個節點的樹,你能夠進行\(n−1\)次操做,每次操做步驟以下:
求一個操做序列使得\(ans\)最大。
解:
先把直徑拿出來,將直徑外的點一個一個的和直徑中的某一個端點配對並刪掉,最後再將直徑刪掉。
證實:
若是當前直徑外已經沒有點了,那麼顯然只能將直徑的端點刪掉;不然必定不會去刪直徑的端點。
由於先刪一個直徑外的點再刪直徑端點必定是不劣於先刪直徑端點再刪這個直徑外的點的。
好比有這樣三個點\(x,y,z\),\(x,z\)是該樹直徑的兩端,
假設刪除順序是\(x,y\),那麼\(ans+=dis[x][z]+dis[y][z]\)
假設刪除順序是\(y,x\),那麼\(ans+=\max(dis[x][y],dis[y][z])+dis[x][z]\)
給定一張\(n\)個點\(m\)條邊的帶權無向聯通圖,\(q\)次詢問,每次詢問\(u_i\)到\(v_i\)的最短路長度
\(n,q\le 10^5,m-n\le 20\)
解:
注意到\(m-n\le 20\),那麼這張圖其實就是一個樹多了屈指可數的幾條非樹邊
先隨便搞一棵生成樹,那麼會有幾條邊不在這個生成樹上,標記那些有非樹邊鏈接的點
那麼被標記的點最多\(40\)個,先跑\(40\)遍單源最短路求出這些被標記的點到其他全部點的最短路
對於一個詢問,若是最短路(跑\(LCA\)來求)只通過生成樹上的邊,那麼直接計算;若通過了被標記的點,就查看走這個點鏈接的非樹邊是否會對答案形成影響,嘗試更新就好了
給定一棵\(n\)個節點的樹,定義葉子是度數爲\(1\)的節點,點集\(S\)是好的若是知足任意兩個\(S\)中的點之間的距離\(\le k\),如今要求將全部葉子分紅若干不相交的好的集合,請問最少分紅多少個好的集合。
\(n,k\le 10^6\)
解:
講師說這類題目通常均可以貪心,我也不知道是哪類題目。。。(話說樹上題目有什麼分類的麼orz)
先隨便選一個根,方便起見選一個不是葉子的根,而後能夠自底向上貪心。
設\(f_p\)表示\(p\)爲根的子樹內已完成的好的集合的數量,\(g_p\)表示未完成的集合中離\(p\)最遠的葉子的距離。
考慮如何計算\(f_p\)和\(g_p\)。
首先將全部兒子的\(f\)先求和,而後將全部兒子的\(g\)排序,檢查 一下最大的\(g\)和次大的\(g\)相加是否大於\(k\),若是大於,那麼就將最大的\(g\)刪掉,並把\(f_p\)加上\(1\)。不然就令\(g_p\)等於最大的\(g+1\)。
給定\(n\)個節點,每一個節點上有一個數字,要求用這些節點構建一棵二叉搜索樹,知足有邊相鄰的兩個節點上的數字互質
\(n\le 600\)
解:
先排序,令\(f[i][j][k]\)表示第\(i\)個點到第\(j\)個點以\(k\)爲根是否可行
轉移的時候首先檢查是否存在\(u\in [i,k-1]\)知足\(f[i][k-1][u]=1\)且\(u\)上的數字跟\(k\)上的數字互質
右側同理,檢查是否存在\(v\in [k+1,j]\)知足\(f[k+1][j][v]=1\)且\(v\)上的數字跟\(k\)上的數字互質
可是這樣是\(O(n^4)\)的(數組是\(n^3\)的,轉移是\(n\)的)
考慮優化:
注意到上面的作法有一些重複運算。因而設\(L[i][j]\)表示是否存在\(k\in [i,j]\)知足\(f[i][j][k]=1\)且\(k\)上的數字和\(j+1\)上的數字互質,相似地定義\(R[i][j]\)