jzyz 題庫 選作 及 知識小結

校oj不少題目仍是很好的 考慮給他們蟹蟹題解Yios

更新於:10.24c++

打掃衛生 題目大意:n個房間 m個同窗 每一個房間至少兩個同窗 求方案數git

顯然是個計數類的題目 技術類題目 真的 須要有必定數學感受8... 頭有點大奧 數組

而後咱們考慮這道題目顯然是一個組合數的問題 而後須要用到隔板法 因此在這裏 我淺析一下隔板法的應用 有多學習了一個好東西網絡

具體隔板法 對於n個有順序的球 咱們考慮在中間放入m個板 而後規定咱們不能在兩個球之間放兩個及以上個隔板 而後兩段不能放隔板 問你方案數數據結構

咱們不妨考慮 在這n-1個空位置 放入m個球 那麼方案數 顯然是$\binom{n-1}{m}$ide

聽說今年高聯數學二試也考了個隔板法?? 仍是比較好寫的那個題目 究竟是形如什麼樣的式子 能夠 使用隔板法求解問題呢學習

咱們不妨舉幾個例子 優化

求解$\sum_{i=1}^{m} x_i=n$ 有多少個正整數ui

對於這種狀況 咱們能夠考慮是在n個球中插入了 m-1個板 類比剛纔的方法  容易獲得方案數數爲$\binom{n-1}{m-1}$ 

而後考慮 對於求解$\sum_{i=1}^{m} x_i=n$ 有多少個天然數解 也就是說 你能夠存在$x_i=0$ 因此咱們爲了不這種狀況出現 

咱們能夠對於每個$x_i+1$ 因此 咱們的式子變成了 $\sum_{i=1}^{m} (x_i+1)=n+m$ 因此此時換元 每個$t_i$ 就是正整數了

因此咱們能夠類比剛纔的方法作了 而後方案數就是$\binom{n+m-1}{m-1}=\binom{n+m-1}{n}$ 

而後這道題目保證每一個房間至少兩我的 因此咱們不妨 先將這 n*2個同窗T出去 由於必定要留下來這2*n個同窗 至於剩下的m-2*n個同窗

咱們考慮 轉化成剛纔咱們討論過的問題 咱們除去這些2*n我的以後 咱們 每一個房間的人數$x_i$ 這時候就是求解 $\sum_{i=1}^{n} x_i=m-2*n$

對於這種狀況 咱們$x_i$ 存在 等於0的狀況 咱們進一步 轉化成 整數狀況 $\sum_{i=1}^{n} (x_i+1)=m-2*n+n$ 而後類比剛纔的例子1 咱們就求出了答案

因此這個題目 還有寫 高精*單精 就沒了

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch))     {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int N=1010; int n,m,top,b[N],p0[N],p1[N],p2[N]; inline void insert(int x,int *a) { int now=x; for(int i=2;i*i<=x;i++) { while(now%i==0) { now=now/i; ++a[i]; } } if(now>1) ++a[now]; } inline void mul(int x) { int res=0; for(int i=1;i<=top;i++) { b[i]=b[i]*x; b[i]+=res; res=b[i]/10; b[i]=b[i]%10; } while(res) { b[++top]=res; res=b[top]/10; b[top]=b[top]%10; } } int main() { read(n); read(m); if(m<2*n) {puts("0");return 0;} int s=m-2*n+n-1,c=m-2*n; for(int i=2;i<=s;i++) insert(i,p0); for(int i=2;i<=c;i++) insert(i,p1); for(int i=2;i<=s-c;i++) insert(i,p2); b[++top]=1; for(int i=2;i<=s;i++) { p0[i]=p0[i]-p1[i]-p2[i]; while(p0[i]) { --p0[i]; mul(i); } } for(int i=top;i>=1;i--) printf("%d",b[i]); return 0; }
View Code

pigs 一道頗有意思的網絡流呢 這個建圖 我着實 思考了一會 不過在Chdy的指導下 仍是搞出來了 是個最大流

具體怎麼建圖呢 由於很是容易想到的是 咱們確定是源點向每一個豬小屋連邊 容量是每一個豬小屋原來擁有的豬豬數量 而後 每一個人向匯點連邊

而後容量是每一個人 原來想要購買的 豬豬的數量 那麼考慮 豬小屋 和 顧客之間怎麼連邊 咱們考慮一個事情是 每一個豬小屋第一個打開他的 顧客是必定的

當前這個顧客購買完以後 咱們考慮 如何將當前這個小屋的豬進行一個轉移 就是指向一個接下來能打開他的顧客 因此咱們對於豬小屋和顧客 

咱們將每個豬小屋 和第一個能打開他的 顧客 連邊 容量是 inf 而後將當前第一個顧客 和 可以再次打開這個豬小屋的顧客連邊 而後咱們就實現了 豬的轉移 

跑一邊最大流便可 我我我我最大流仍是寫錯了 也就是=寫成==不報錯唄hh 

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch))     {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int inf=0x7fffffff; const int N=1000010; struct gg { int y,v,next; }a[N<<1]; int n,m,x,y,z,tot,s,t,H,T,lin[N],q[N],d[N],st[N]; inline void add(int x,int y,int v) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; a[tot].v=v; a[++tot].y=x; a[tot].next=lin[y]; lin[y]=tot; a[tot].v=0; } inline bool bfs() { memset(d,0,sizeof(d)); H=T=0;q[++T]=s;d[s]=1; while(H++<T) { int x=q[H]; for(int i=lin[x];i;i=a[i].next) { if(a[i].v&&!d[a[i].y]) { q[++T]=a[i].y; d[a[i].y]=d[x]+1; if(a[i].y==t) return 1; } } } return 0; } inline int dinic(int x,int flow) { if(x==t) return flow; int rest=flow,k; for(int i=lin[x];i&&rest;i=a[i].next) { if(a[i].v&&d[a[i].y]==d[x]+1) { k=dinic(a[i].y,min(rest,a[i].v)); if(!k) d[a[i].y]=0; a[i].v-=k;a[i^1].v+=k; rest-=k; } } return flow-rest; } int main() { // freopen("1.in","r",stdin);
    tot=1; read(m); read(n); s=1,t=2+n+m; for(int i=1;i<=m;i++) { read(x); add(1,i+1,x); } for(int i=1;i<=n;i++) { read(x); for(int j=1;j<=x;j++) { read(y); if(!st[y]) add(1+y,m+i+1,inf),st[y]=i+m+1; else add(st[y],m+i+1,inf); } read(z); add(m+i+1,t,z); } int flow=0,max_flow=0; while(bfs()) { while(flow=dinic(s,inf)) max_flow+=flow; } cout<<max_flow<<endl; return 0; } 
View Code

 [8.28]機率遊戲  

好像在模擬賽總結的時候 寫過這個題目 因此直接樹狀數組就好了 或者排序後二分 樹狀數組開了map 因此比較慢

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch))     {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } typedef long long ll; const int N=1000010; ll n,X,a[N]; map<ll,ll>c; inline ll lowbit(ll x) { return x&(-x); } inline void add(ll x) { for(ll i=x;i<=10001000;i+=lowbit(i)) c[i]+=1; } inline ll query(ll x) { ll res=0; for(ll i=x;i>0;i-=lowbit(i)) res+=c[i]; return res; } int main() { //freopen("1..cpp","r",stdin);
 read(n); read(X); for(int i=1;i<=n;i++) { read(a[i]); add(a[i]); } ll sum=0,t=n*(n-1); for(int i=1;i<=n;i++) { sum+=query(X-a[i]); if(a[i]<=X-a[i]) --sum; } double ans=(double)sum/t; printf("%.2lf",ans); return 0; }
View Code

 [6.24]子序列累加和 不是連續子序列啊qwq

一眼望去 我想枚舉區間 可是這種區間最值得東西 咱們不妨思考一種數據結構 這個題目是要咱們求最大值最小值的差值

由於咱們都知道答案是$\sum_{i=1}^{n} MAX[i]*a[i]-MIN[i]*a[i]$ 其中MAX[i]表示 以i做爲區間最大值 的區間的個數 MIN[i]表示以i做爲區間最小值的區間個數

考慮$MAX[i]$這些數組怎麼求出來 咱們能夠類比樓蘭圖騰那道題目 求出$left[i]表示當前這個元素向左邊是多少個區間的最值 right[i]$同理 由於本身也能夠產生貢獻

因此答案是$left[i]*right[i]+1$ 因此咱們考慮用單調棧來維護這個東西 具體怎麼作呢

以一個例子爲例 求出一個元素左邊做爲多少個區間的最小值 那麼

咱們顯然是須要維護一個 單調遞增的棧 而後咱們考慮 當前這個元素比棧頂大 那麼就加入

不然 不斷彈出棧 那麼當前的貢獻就是棧頂的貢獻+1 而後 這裏須要考慮的是 你的while循環裏 只能有兩個等號 也就是同一個方向的是一個等號

不能全是 也不能全不是 這一點要處理好 爲何呢 咱們是爲了 避免 漏掉 以及 重複的狀況 思考一下

//如今有N個數的數列。如今你定義一個子序列是數列的連續一部分 //子序列的值是這個子序列中最大值和最小值之差 //求全部子序列的值得累加和
#include<bits/stdc++.h>
using namespace std; typedef long long LL; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=((x<<1)+(x<<3)+(ch^48));ch=getchar();} x*=f; } const int N=300010; LL n; LL a[N],s1[N],s2[N],s3[N],s4[N],sum1[N],sum2[N],sum3[N],sum4[N]; LL top1=0,ans=0,top2=0,top3=0,top4=0; int main() { //freopen("1.in","r",stdin);
 read(n); for(int i=1;i<=n;i++) { read(a[i]); } s1[++top1]=1; s2[++top2]=n; s3[++top3]=1; s4[++top4]=n; for(LL i=2;i<=n;i++) { for(;a[i]<a[s1[top1]]&&top1;--top1) sum1[i]+=sum1[s1[top1]]+1; s1[++top1]=i; }//正序最小
    for(LL i=n-1;i;i--) { for(;a[i]<=a[s2[top2]]&&top2;--top2) sum2[i]+=sum2[s2[top2]]+1; s2[++top2]=i; }//倒序最小
    for(LL i=2;i<=n;++i) { for(;a[i]>a[s3[top3]]&&top3;--top3) sum3[i]+=sum3[s3[top3]]+1; s3[++top3]=i; }//正序最大
    for(LL i=n-1;i;--i) { for(;a[i]>=a[s4[top4]]&&top4;--top4) sum4[i]+=sum4[s4[top4]]+1; s4[++top4]=i; }//倒序最大
    for(LL i=1;i<=n;++i) ans+=(((sum3[i]+1)*(sum4[i]+1))-((sum1[i]+1)*(sum2[i]+1)))*a[i]; cout<<ans<<endl; return 0; }
View Code

 [10.21]調整公約數

原本看到 沒有思路 可是咱們不妨思考一下 每次老是一個數字 除以一個質因數 另外一個是 乘上質因數 因此 咱們發現 全部數字乘起來

對應的質因數分解 後 質因子對應的指數是不變 因此咱們考慮 對於每個 $a_i$ 咱們進行質因數分解 記錄一下  每個數字 對應的質因數 出現的次數

而後 記錄一下 這個質因數 一共出現的次數 

接下來 咱們要保證次數最少 確定是考慮 將最小的質因數 的指數 均分 給每個數字了 而後 咱們找到這樣的數字 能夠保證這樣的數字必定是存在的 不然最大公約數就是1

可是須要解決的問題還有一個就是空間問題 我第一遍的作法 是 直接MLE了 因此我考慮 改了一下 當前狀態 而後開了一個map 解決了這個事情

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } typedef long long ll; int vis[1001000]; int x,n; map<int,map<int,int> >a; inline ll mul(int a,int b) { ll res=0; while(b) { if(b&1) res=(res+a); a=(a+a); b>>=1; } return res; } inline ll power(int a,int b) { ll res=1; while(b) { if(b&1) res=(res*a); a=mul(a,a); b>>=1; } return res; } int main() { //freopen("1.in","r",stdin);
 read(n); int ans=1,sum=0,jud; for(int i=1;i<=n;i++) { read(x); for(int j=2;j<=sqrt(x*1.0);j++) { while(x&&x%j==0) { x/=j; ++vis[j]; ++a[i][j]; } } ++vis[x]; a[i][x]++; } for(int i=2;i<=1000000;i++) { jud=vis[i]/n; if(jud) { ans=ans*power(i,jud); for(int j=1;j<=n;j++) { if(a[j][i]<jud) sum+=jud-a[j][i]; } } } printf("%d %d",ans,sum); return 0; }
View Code

9.8]玻璃球遊戲 

這顯然是一個 並差集的題目 可是 須要並差集的斷開 操做 因此咱們不妨 將操做離線 而後 將斷開 改爲插入 而後 正難則反 好像這個道理只有我剛知道

這裏%一下chdy 和 一刀一個小朋友 兩個選手 最後 本校oj 須要手動開棧qwq

#include<bits/stdc++.h>
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 int read() { int This=0,F=1; char ch=getc(); while(ch<'0'||ch>'9') {if(ch=='-') F=-1;ch=getc();} while(ch>='0'&&ch<='9') {This=(This<<1)+(This<<3)+ch-'0';ch=getc();} return This*F; } void put(int x) { if(x==0) { putchar('0'); putchar('\n'); return; } if(x<0) { putchar('-'); x=-x; } int num=0;char ch[16]; while(x) ch[++num]=x%10+'0',x/=10; while(num) putchar(ch[num--]); putchar('\n'); } const int N=300100; struct gg { int flag,x; }a[N]; int n,q,ans[N],Next[N],father[N],vis[N]; inline int find(int x) { if(x==-1) return -1; return father[x]==x?x:father[x]=find(father[x]); } int main() { // freopen("1.in.cpp","r",stdin);
    int __size__ = 20 << 20; // 20MB
    char *__p__ = (char*)malloc(__size__) + __size__; __asm__("movl %0, %%esp\n" :: "r"(__p__)); n=read(); for(register int i=1;i<=n;i++) Next[i]=read(),father[i]=i; q=read(); for(register int i=1;i<=q;i++) { a[i].flag=read(); a[i].x=read(); if(a[i].flag==2) vis[a[i].x]=1; ans[i]-=2; } for(register int i=1;i<=n;i++) { if(!vis[i]&&Next[i]) { int p=find(Next[i]); if(i==p) father[Next[i]]=-1; father[i]=Next[i]; } } for(register int i=q;i>=1;i--) { if(a[i].flag==1) ans[i]=find(a[i].x); else { int x=a[i].x; int y=find(Next[x]); if(x==y) father[Next[x]]=-1; father[x]=Next[x]; } } for(register int i=1;i<=q;i++) { if(ans[i]==-1) printf("CIKLUS\n"); else if(ans[i]!=-2) put(ans[i]); } return 0; } 
View Code

數列操做(差分)問題

既然題目已經給提示 是差分了 因此咱們能夠往差分的思想上思考 而後咱們不妨思考一下 

對於這種對原序列的一段區間$l,r$進行+1,-1的操做 加入對於原序列 $a_l,a_r$ 進行+1 咱們能夠對應到差分序列上 就是$d_l$ 數組 +1 而後 $d_{r+1}$-1 因此對於這種執行區間操做 我

們就轉化成了 對差分序列進行兩個數字的操做

對於使得最後的數組相同也就是 從差分序列的第2項到第n項 最終變成0的最少操做數 而後咱們思考 一下

對於差分序列 咱們爲了使其變成0 咱們能夠每次選擇一個正數-1 而後選擇一個負數+1 而後 咱們求出 第二項到第n項的 正數和爲 p 負數的絕對值的和爲 q

而後 顯然操做數就是 $max(p,q)$ 而後 對於 可能的序列數 咱們每次講正數和負數 匹配 而後 咱們思考 將其中一個變成0 這樣的操做次數是$min(p,q)$

此時序列上剩餘 和 爲abs(p-q)的數字 而後考慮和 差分數列的$d_1或者d_{n+1}$ 進行配對 而後 咱們考慮這樣的次數是 abs(p-q)的 而後對於的獲得的序列數

咱們就是考慮最終 $ d_1 $的全部可能的方案數 就是abs(p-q)+1 由於最後d1能夠不變 保持原來的數字

注意開ll 今天我這個同窗由於這個爆零好屢次qwq

//#include<bits/stdc++.h>
#include<iomanip> #include<utility> #include<cctype> #include<vector> #include<deque> #include<map> #include<stack> #include<queue> #include<bitset> #include<set> #include<cstdlib> #include<algorithm> #include<iostream> #include<cstdio> #include<ctime> #include<cmath> #include<cstring> #include<string>
#define INF 2147483646
#define up(p,i,n) for(long long i=p;i<=n;i++)
using namespace std; inline long long read() { long long 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; } inline void put(long long x) { x<0?x=-x,putchar('-'):0; long long num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const long long MAXN=100002; long long a[MAXN],b[MAXN]; long long n; long long ans=0,cnt=0; int main() { n=read(); up(1,i,n)a[i]=read(); up(2,i,n)b[i]=a[i]-a[i-1]; up(2,i,n){if(b[i]<0)ans+=b[i];if(b[i]>0)cnt+=b[i];} ans=abs(ans);cnt=abs(cnt); put(max(ans,cnt)); put(abs(ans-cnt)+1); return 0; }
View Code

 [10.17]揹包練習 

我又把揹包問題忘完了qwq 先複習一下之前的知識8

今天早上作了一個 01揹包的第k優解 其實 這個知識點是很早發現的 但一直沒寫qwq 

咱們設$f_{i j k}$表示 前 i 個物品  佔了 體積爲 j 的揹包時 對應第k優解 的最大價值 根據揹包的性質 咱們顯然 優化掉第一維

因此 有用的狀態就是 $f_j 和 f_{j-v[i]}$ 可是這個時候 他們再也不對應一個個值 而是咱們要把他們當成一個序列 開一個數組記錄 每次的最優解 直到選夠k個

而後考慮 取前面k個 就獲得了 第k優解 可是須要注意的是 咱們的數組初始化負無窮 而後 $f_{0 1}=0$ 只有這一個是合法狀態 

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch))     {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int N=5010; const int M=210; int k,n,m,v[M],w[M],f[N][60],c[60]; int main() { read(k); read(m); read(n); for(int i=1;i<=n;i++) read(v[i]),read(w[i]); memset(f,0xcf,sizeof(f)); f[0][1]=0; for(int i=1;i<=n;i++) { for(int j=m;j>=v[i];j--) { int l=1,r=1,t=0,tmp=0; while(t<k) { if(f[j][l]<f[j-v[i]][r]+w[i]) { c[++tmp]=f[j-v[i]][r]+w[i],r++; } else c[++tmp]=f[j][l],l++; t++; } for(int s=1;s<=k;s++) f[j][s]=c[s]; } } int ans=0; for(int i=1;i<=k;i++) ans+=f[m][i]; printf("%d\n",ans); return 0; } 
View Code

而後咱們考慮 這個題目是讓咱們不選擇 第i個時 裝滿揹包的方案數 啊怎麼寫啊qwq  暴力暴力qwq

qwq 爆零選手 回來寫博客了 顯然根據咱們迴歸到揹包最原來的狀態 $f_j$ 的轉移只有兩個一個是 $f_j 和 f_{j-v[i]}$ 前者表示 不選擇第i個物品 後者表示選擇第i個物品

而後咱們能夠 根據這個進行轉移 咱們先求出 不考慮限制的方案數 而後考慮 不選擇第i個物品 就是咱們將當前的方案數減去強制選擇i的方案數

當進行下一次轉移的時候 咱們 再將 減去的累加回來 由於一次就不選擇一個 複雜度 (nm)  模數寫錯 我又爆零了

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch))     {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } int n,m,v[1010],f[10100]; int main() { //freopen("1.in","r",stdin);
 read(n); read(m); for(int i=1;i<=n;i++) read(v[i]); f[0]=1; for(int i=1;i<=n;i++) { for(int j=m;j>=v[i];j--) { f[j]+=f[j-v[i]]; f[j]%=1014; } } for(int k=1,j=v[k];j<=m;j++) {//單獨處理第一個 
        f[j]-=f[j-v[k]]; f[j]%=1014; } cout<<(f[m]+1014)%1014<<' '; for(int k=1;k<=n;) { for(int j=m;j>=v[k];j--) { f[j]+=f[j-v[k]]; f[j]%=1014; } k++; for(int j=v[k];j<=m;j++) { f[j]-=f[j-v[k]]; f[j]%=1014; } cout<<(f[m]+1014)%1014<<' '; if(k==n) return 0; } }
View Code

最近寫了一道貪心 比較水 可是 剛開始T了 後來又寫了一遍 老是有這樣一堆 板子的 貪心題目 證實我沒有咕這個博客

[10.14]小紅花 

那麼顯然看出貪心 就是按照 右端點從小到大排序 每次 從右 向 左 放 沒有就放 有就跳過 其實不是說這個題有多巧妙 模型就是 若干個區間 限制每一個區間最少選擇多少個點 而後考慮選擇最少的點是多少

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch))     {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int N=5100; struct gg { int l,r,x; }a[N]; inline bool mycmp(gg x,gg y) { return x.r==y.r?x.l<y.l:x.r<y.r; } int n,m; bool p[310000]; int main() { //freopen("1.in","r",stdin);
    scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].x); } sort(a+1,a+m+1,mycmp); int ans=0; for(int i=1;i<=m;i++) { int v=a[i].x,r=a[i].r; for(int j=a[i].l;j<=a[i].r;j++) { if(p[j]) v--; } for(;v>0;r--) { if(!p[r]) { p[r]=1; v--; ans++; } } } cout<<ans<<endl; } 
View Code

是最近我發現好多貪心 都是這樣

好比 雷達監測 咱們轉化模型以後 發現就是若干個 區間 而後你選擇 最少的點 st 全部的區間都至少有一個點 被選中

將全部區間按右端點從小到大排序 依次考慮每一個區間

若是當前區間包含最後一個選擇的點,則直接跳過

若是當前區間不包含最後一個選擇的點 則在當前區間的右端點的位置選一個新的點 選擇 右端點 是最優 不會更差的作法 仔細理解這個貪心的過程

#include<bits/stdc++.h>
using namespace std; typedef pair<double,double> PDD; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const double eps=1e-9; const int inf=1e10; const int N=1010; PDD g[N]; int n,d,x,y; int main() { //freopen("1.in","r",stdin);
 read(n); read(d); int flag=0; for(int i=1;i<=n;i++) { read(x); read(y); if(y>d) { flag=1; break; } double len=sqrt(d*d-y*y); g[i]={x+len,x-len}; } if(flag) { puts("-1"); return 0; } else { sort(g+1,g+n+1); int res=0; double last=-inf; for(int i=1;i<=n;i++) { if(g[i].second>last+eps) { res++; last=g[i].first; } } cout<<res<<endl; return 0; } }
View Code

還好比 蓄欄預約 每一個牛有個吃草時間區間 而後 區間有重疊的 不能在一個 蓄欄 考慮最少準備多少個 蓄欄

那麼這個就是 按照左端點從小到大排序 而後 開一個小根堆 維護一個右端點 每次判斷 右端點是否位於下一個區間裏 若是是 那麼 考慮此時累加 個數 而且放進堆裏 維護此時 把位置更新

若是不是 那麼安排放在一塊兒  都是acwing上的貪心題目

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int N=50010; struct gg { int l,r,id; }a[N]; struct pink { int r,size; /*int operator < (pink &a) const{ return a.r<r; }*/ 
    int friend operator <(pink a,pink b) { return a.r>b.r; } }; inline bool mycmp(gg x,gg y) { return x.l==y.l?x.r<y.r:x.l<y.l; } priority_queue<pink>q; int n,pos[N]; int main() { //freopen("1.in","r",stdin);
 read(n); for(int i=1;i<=n;i++) { read(a[i].l); read(a[i].r); a[i].id=i; } sort(a+1,a+n+1,mycmp); for(int i=1;i<=n;i++) { if(q.empty()||q.top().r>=a[i].l) { pink h; h.r=a[i].r;h.size=q.size(); q.push(h); pos[a[i].id]=h.size; } else { pink h=q.top(); q.pop(); pos[a[i].id]=h.size; h.r=a[i].r; q.push(h); } } cout<<q.size()<<endl; for(int i=1;i<=n;i++) cout<<pos[i]+1<<endl; return 0; }
View Code

[8.21]8的倍數

這個題 看到 嗚嗚嗚 只會寫暴力 考慮怎麼優化這個題目呢 思考了一會 發現沒有什麼思路

那麼考慮 此時 我去問了一下強者 強者跟我說 容斥一下就好了 不會 出去和老師恰了個飯 一餐的方便麪還不錯 回到機房 等待晚上講 前四場的模擬賽題解 那麼思考了一下 發現了 其中的規律

其實 咱們能夠預處理 1-y 的知足條件的 個數 和1 -  x-1 的個數 而後兩者相減 就是個數 

考慮數論了 顯然是2^n 枚舉因子的使用狀況 那麼尋找當前使用因子和8的lcm 根據容斥 那個神奇的例子 咱們奇減偶加 而後用x-1或者y除以這個lcm 而後就求出了出現個數

後來發現能夠dfs 其實二進制枚舉就能夠

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } typedef long long ll; const int N=20; int n,m,x,y,a[N]; inline ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } inline ll ask() { ll ans=0; for(int i=0;i<(1<<n);i++) { ll tot=0,num=8; for(int j=0;j<n;j++) { if(i&(1<<j)) { num*=a[j]/gcd(a[j],num); if(num>y) break; tot++; } } if(tot&1) ans-=y/num; else ans+=y/num; } return ans; } inline ll ask1() { ll ans=0; for(int i=0;i<(1<<n);i++) { ll tot=0,num=8; for(int j=0;j<n;j++) { if(i&(1<<j)) { num*=a[j]/gcd(a[j],num); if(num>(x-1)) break; tot++; } } if(tot&1) ans-=(x-1)/num; else ans+=(x-1)/num; } return ans; } int main() { read(n); for(int i=0;i<n;i++) read(a[i]); read(x); read(y); printf("%lld\n",ask()-ask1()); return 0; } 
View Code

免費餡餅

長了個dp的樣子 確實這個dp 正推 倒推均可以 這裏我寫了倒推 咱們設狀態$dp[i][j]$表示在第 i 個時刻 位於 j 地點的最大分數

那麼咱們這個時候要求的終點狀態就是dp[0][w/2+1] 由於最開始位於舞臺中央

因此 $dp[i][j]=dp[i+1][j+k]$ k 表示移動方向

那麼咱們倒序循環 時刻 以及 舞臺位置 每一個方向取最大 有一點 能不能不動 因此優先判斷不動

對於方案輸出 咱們只須要再次模擬一遍 尋找答案的過程 而後記錄一下 方向輸出便可 

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0; T f=1,ch=getchar(); while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar(); x*=f; } const int N=1100; int n,m,w,h,now; struct gg { int tim,pos,speed,val; }a[N]; int f[N][N];//f[i][j]表示當前是i時刻位於j地點撿到的最大分數 
inline int slove(int tim,int pos) { int ans=0; if(f[tim+1][pos]>ans) { ans=f[tim+1][pos]; now=0; } if(f[tim+1][pos-2]>ans&&pos-2>0) { ans=f[tim+1][pos-2]; now=-2; } if(f[tim+1][pos-1]>ans&&pos-1>0) { ans=f[tim+1][pos-1]; now=-1; } if(f[tim+1][pos+1]>ans&&pos+1<=w) { ans=f[tim+1][pos+1]; now=1; } if(f[tim+1][pos+2]>ans&&pos+1<=w) { ans=f[tim+1][pos+2]; now=2; } return ans; } int main() { //freopen("1.in","r",stdin);
    read(w); read(h); h--; int n=1; while(scanf("%d%d%d%d",&a[n].tim,&a[n].pos,&a[n].speed,&a[n].val)==4) { if((h%a[n].speed==0)||a[n].tim==0) { a[n].tim+=(h/a[n].speed); m=max(m,a[n].tim); n++; } }--n; memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { f[a[i].tim][a[i].pos]+=a[i].val; } for(int i=m-1;i>=0;i--) { for(int j=1;j<=w;++j) { f[i][j]+=slove(i,j); } } cout<<f[0][w/2+1]<<endl; for(int i=0,j=w/2+1;;++i) { if(!slove(i,j)) break; j+=now; cout<<now<<endl; } return 0; } 
View Code

 [8.20]校門外的樹(增強版)

我也不知道 爲何我要 放出來 反正我以爲是個線段樹 區間修改 區間查詢 很好寫  或許我只是想放個代碼

#include<bits/stdc++.h>
using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch))     {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int N=100100; int n,m,x,y; struct gg { int l,r,tag,sum; #define tag(p) t[p].tag
    #define l(p) t[p].l
    #define r(p) t[p].r
    #define sum(p) t[p].sum  }t[N*4]; inline void build(int p,int l,int r) { if(l==r) { sum(p)=1; return ; } int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); sum(p)=sum(p<<1)+sum(p<<1|1); } inline void pushdown(int p) { if(tag(p)) { tag(p<<1)=1; tag(p<<1|1)=1; sum(p<<1)=0; sum(p<<1|1)=0; tag(p)=0; } } inline void change(int p,int l,int r,int ll,int rr) { if(ll<=l&&rr>=r) { tag(p)=1; sum(p)=0; return ; } pushdown(p); int mid=(l+r)>>1; if(ll<=mid) change(p<<1,l,mid,ll,rr); if(rr>mid) change(p<<1|1,mid+1,r,ll,rr); sum(p)=sum(p<<1)+sum(p<<1|1); } int main() { //freopen("1.in","r",stdin);
 read(m); read(n); build(1,1,n); for(int i=1;i<=m;i++) { read(x); read(y); change(1,1,n,x,y); printf("%d\n",t[1].sum); } return 0; } 
View Code
相關文章
相關標籤/搜索