寫省選的題目對noip沒什麼大用 關鍵是 細節題或者是思考題比較重要 練思惟天然是CF比較好了 把我見到的比較好的CF題放上來刷一刷。ios
LINK:Complete the projects c++
就是說一我的 初始值爲R 有n項工做每項工做有兩種屬性 a和b 當且僅當 R>=a時能夠完成這項任務 且R+=b; 每項任務只能完成一次 問可否把全部工做完成。注:b可能爲負。算法
怎麼作?顯然對於b>=0的工做咱們按照a由小到大的順序直接作若是有不能完成的任務的話 那便必定不能完成。考慮如何作負值的工做?數組
想了一下咱們不能僅僅按照a的值從大到小作 由於可能出現順序上的錯誤 致使答案錯誤 按照 ai+bi從大到小的順序麼? 通過個人一番操做 發現wa掉了。再次考慮這個順序?ide
苦苦思索好多天 發現沒認真讀題 題目中說道對於完成每個任務都R都得非負,最後我沒判斷R的正負... 因此就是按照ai+bi從大到小的順序的。優化
當時考的時候我直接想到了 可是沒想證實這裏來利用微擾法來證實一下。設當前任務排序爲 a1+b1,a2+b2...an+bn;spa
若是對於 ai+bi >aj+bj 來講j排在i前面那麼 則有R>=aj 且R-=bj 以後可得 R>=ai; 咱們看另外一種狀況 R>=ai 且 R-=bi 以後又 R>=aj;翻譯
好像不太好證實 由於看起來很是的顯然 也容易想到,那麼不證自明好了.../cy3d
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<ctime> #include<cstring> #include<string> #include<ctime> #include<cctype> #include<cstdio> #include<utility> #include<queue> #include<stack> #include<deque> #include<map> #include<set> #include<bitset> #include<vector> #include<algorithm> #include<cstdlib> #define ll long long 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 ll MAXN=210; ll n,r,top,top1,flag; struct wy { ll x,y; }t[MAXN],w[MAXN]; inline ll cmp(wy a,wy b){return a.x<b.x;} inline ll cmp1(wy a,wy b){return a.x+a.y>b.x+b.y;} int main() { //freopen("1.in","r",stdin); n=read();r=read(); for(ll i=1;i<=n;++i) { ll x,y; x=read();y=read(); if(y>=0)t[++top]=(wy){x,y}; if(y<0)w[++top1]=(wy){x,y}; } sort(t+1,t+1+top,cmp); for(ll i=1;i<=top;++i) { if(t[i].x>r){puts("NO");return 0;} r+=t[i].y; } sort(w+1,w+1+top1,cmp1); for(ll i=1;i<=top1;++i) { if(w[i].x>r){puts("NO");return 0;} r+=w[i].y; } if(r>=0)puts("YES"); else puts("NO"); return 0; }
LINK:增強版code
如今是求 咱們最多能完成的任務數 這就頗有區別了首先對於b>=0咱們不必定能所有作完可是仍是能夠貪心的去作 由於拿不到的無論採起什麼方法都拿不到。
考慮如何取 a<0的部分此次咱們不能再貪心的選取了 由於 可能當前是能選致使R大大減少 以致於後面選的任務更少了。
可是咱們仍能夠按照上一道題的排序方式來選取,這樣仍可使得利益最大化...固定了順序接下來的操做其實就是對於每一個任務選或不選了,直接揹包的拿取便可。dp一下...
固然和揹包不太同樣由於 有ai的限制咱們狀態設爲 到第i個任務此時R值爲j所作的最多的任務數,這樣進行轉移便可。只不過這個須要刷表法更容易寫。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<ctime> #include<cstring> #include<string> #include<ctime> #include<cctype> #include<cstdio> #include<utility> #include<queue> #include<stack> #include<deque> #include<map> #include<set> #include<bitset> #include<vector> #include<algorithm> #include<cstdlib> #define ll long long 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 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=110,maxn=30010<<1; int n,r,top,top1,ans,maxx; int f[MAXN][maxn];//f[i][j]表示到了第i個任務此時還有j所能完成的最大任務數 struct wy { int x,y; }t[MAXN],w[MAXN]; inline int cmp(wy a,wy b){return a.x<b.x;} inline int cmp1(wy a,wy b){return a.x+a.y>b.x+b.y;} int main() { //freopen("1.in","r",stdin); n=read();r=read(); for(int i=1;i<=n;++i) { int x,y; x=read();y=read(); if(y>=0)t[++top]=(wy){x,y}; if(y<0)w[++top1]=(wy){x,y}; } sort(t+1,t+1+top,cmp); for(int i=1;i<=top;++i) { if(t[i].x>r)break; r+=t[i].y;++ans; } sort(w+1,w+1+top1,cmp1); for(int i=1;i<=top1;++i) { for(int j=0;j<=r;++j) { f[i+1][j]=max(f[i+1][j],f[i][j]); if(j>=w[i].x&&j>=w[i].y)f[i+1][j+w[i].y]=max(f[i+1][j+w[i].y],f[i][j]+1); } } for(int j=0;j<=r;++j)maxx=max(maxx,f[top1+1][j]); printf("%d\n",maxx+ans); return 0; }
每次 都尋找到任意一個數除以二問 獲得k個相同的數字最小的除法次數。咱們顯然的一個想法是答案是不具單調性的不能二分 不如枚舉 當前答案。
那麼咱們只須要的到 全部元素到這個數字的前k小的和便可,每次都這樣作事n^2logn的 不妨作一次直接存起來而後調用預處理nlogn 對於每一個值開vector存就行了。
而後取前 k個排序便可,累和統計答案便可。由於 數字的個數最多nlogn 因此 複雜度n(logn)^2 可是 我開的是堆每次插入刪除都是logn的因此總複雜度有兩倍的常數。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<ctime> #include<cstring> #include<string> #include<ctime> #include<cctype> #include<cstdio> #include<utility> #include<queue> #include<stack> #include<deque> #include<map> #include<set> #include<bitset> #include<vector> #include<algorithm> #include<cstdlib> #define ll long long #define INF 1000000000 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 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=200010; int n,k; int maxx,w=INF; priority_queue<int>q[MAXN]; int main() { //freopen("1.in","r",stdin); n=read();k=read(); for(int i=1;i<=n;++i) { int x=read(),cnt=0; maxx=max(maxx,x); while(x) { q[x].push(-cnt); x=x>>1;++cnt; } q[0].push(-cnt); } for(int i=maxx;i>=0;--i) { if(q[i].size()<k)continue; int cnt=k,ans=0; while(cnt) { ans+=-q[i].top(); q[i].pop();--cnt; } w=min(w,ans); } printf("%d\n",w); return 0; }
LINK:RBG的子串 這個題還不錯吧,CF主要考的就是思惟了..
可是這裏明確指明瞭是連續的子串 就是說給定一個串讓你改變其中若干個字符 使得長度爲k的字符串既是RBGRBGRBG...的連續子串也是給定的字符串的連續子串。
求要改變的最小數量。初看以爲不太可作..還在思考中。上了趟廁所想起來了 $\sum{n}$<=2000
那麼這樣的話這不是一道**題麼?發現每一個字母都後面緊跟的字母是必定的咱們分析答案的來源 源於某個點開始進行的匹配咱們直接枚舉而後暴力匹配便可.
複雜度3*n*k;
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double 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 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 int MAXN=2010; int n,k,T,ans; char a[MAXN]; int b[MAXN]; int main() { //freopen("1.in","r",stdin); T=read(); while(T--) { n=read();k=read();ans=INF; scanf("%s",a+1); for(int i=1;i<=n;++i) { if(a[i]=='G')b[i]=2; if(a[i]=='B')b[i]=0; if(a[i]=='R')b[i]=1; } for(int i=1;i<=n-k+1;++i)//枚舉起點 { for(int j=0;j<=2;++j) { int w=j,cnt=0; for(int l=i;l<=i+k-1;++l) { if(w==3)w=0; if(b[l]!=w)++cnt; ++w; } ans=min(ans,cnt); } } printf("%d\n",ans); } return 0; }
固然 如今看起來是很水的..可是固然仍是有增強版的Y。
LINK:RBG substring hard version n,k<=200000 固然仍是T組數據 因此 $\sum{n}$<=200000
暴力顯然T,考慮一下正解吧,其實咱們發現能夠優化上述的過程,一個狀態被重複計算了屢次咱們優化便可。
不妨設f[i][j]表示以第i個點爲起點它是j所造成長度爲k的子串的最小代價。仍是比較容易求的吧複雜度這樣就變成了O(n*3);
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double 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 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 int MAXN=200010; int n,k,T,ans; char a[MAXN]; int b[MAXN]; int f[MAXN][3]; int main() { //freopen("1.in","r",stdin); T=read(); while(T--) { n=read();k=read();ans=INF; scanf("%s",a+1); for(int i=1;i<=n;++i) { if(a[i]=='G')b[i]=2; if(a[i]=='B')b[i]=0; if(a[i]=='R')b[i]=1; } for(int i=n;i>=n-k+1;--i) for(int j=0;j<=2;++j) { f[i][j]=b[i]==j?0:1; int w=j+1;if(w==3)w=0; f[i][j]+=f[i+1][w]; } ans=min(ans,f[n-k+1][0]); ans=min(ans,f[n-k+1][1]); ans=min(ans,f[n-k+1][2]); for(int i=n-k;i>=1;--i) { //cout<<b[i]<<' '<<i<<endl; for(int j=0;j<=2;++j) { int w=j+1;if(w==3)w=0; f[i][j]=b[i]==j?0:1; f[i][j]+=f[i+1][w]; f[i][j]-=(b[i+k]!=(j+k)%3)?1:0; ans=min(ans,f[i][j]); } //if(ans==0)cout<<i<<endl; } printf("%d\n",ans); } return 0; }
發現作的都是一些D題 應該多作一些F和E題纔對..
LINK:Connected Component on a Chessboard 這裏的Component意思是組成的組成部分 chessboard意思是棋牌 題目的意思是再棋盤上連接組成。
從題目中咱們能夠得知這是一個網格圖 題目中cell被翻譯乘單元格而並不是細胞。就是說構造一個b+w的聯通塊使得白塊個數爲w 黑塊個數爲b 顯然 分類討論一波便可。
爲了防止越界 我從500000這個點開始構造。
#//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define pb push_back #define un unsigned using namespace std; char *ft,*fs,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=200010; int x[MAXN],y[MAXN]; int Q,b,w; int main() { //freopen("1.in","r",stdin); Q=read(); while(Q--) { b=read();w=read(); if(b>w) { int cnt=3*w+1; if(b>cnt){puts("NO");continue;} puts("YES"); x[1]=500000,y[1]=500000; cout<<x[1]<<' '<<y[1]<<endl; for(int i=2;i<=w;++i) { x[i]=x[i-1]-2; y[i]=y[i-1]; cout<<x[i]<<' '<<y[i]<<endl; cout<<x[i]+1<<' '<<y[i]<<endl; --b; } for(int i=1;i<=w;++i) { if(!b)break; cout<<x[i]<<' '<<y[i]-1<<endl; --b; if(!b)break; cout<<x[i]<<' '<<y[i]+1<<endl; --b; } if(b)cout<<x[1]+1<<' '<<y[1]<<endl,--b; if(b)cout<<x[w]-1<<' '<<y[w]<<endl,--b; } else { int cnt=3*b+1; //cout<<cnt<<endl; if(w>cnt){puts("NO");continue;} puts("YES"); x[1]=500000,y[1]=500001; cout<<x[1]<<' '<<y[1]<<endl; for(int i=2;i<=b;++i) { x[i]=x[i-1]-2; y[i]=y[i-1]; cout<<x[i]<<' '<<y[i]<<endl; cout<<x[i]+1<<' '<<y[i]<<endl; --w; } for(int i=1;i<=b;++i) { if(!w)break; cout<<x[i]<<' '<<y[i]-1<<endl; --w; if(!w)break; cout<<x[i]<<' '<<y[i]+1<<endl; --w; } if(w)cout<<x[1]+1<<' '<<y[1]<<endl,--w; if(w)cout<<x[b]-1<<' '<<y[b]<<endl,--w; } } }
LINK:最短路相關去掉路徑問題 具體題面都是能夠打開的這裏再也不贅述。考慮怎麼作?一個比較容易獲得的錯誤的思路是 先不跑鐵路 先跑道路而後判斷某個鐵路是否可以去掉 而後考慮獲得 一條鐵路也會對整張圖有貢獻考慮用堆 從小到大 判斷 再若是不去掉那麼加到圖中 相似於動態加邊的spfa 可能複雜度不太對qaq. 正確性?很遺憾是錯誤的 由於邊權多是相同的 因此你有可能根本不知道到底哪一條邊先出堆可能消除的邊會更多。發現本身有點蠢怎麼可能出現這種狀況啊。邊權不爲負權是不可能出現我剛纔說的東西的。
因此作法是錯誤的 固然這個動態加邊的spfa 寫的dij複雜度最壞可能會達到nmlogn 不推薦寫還不如直接spfa 因此理所固然的T。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000000000ll #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define F first #define S second #define mk make_pair 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=100010,maxn=400010; int n,m,k,len; int ans; int v[MAXN],vis[MAXN]; int lin[MAXN],nex[maxn<<1],ver[maxn<<1],e[maxn<<1]; ll dis[MAXN]; inline void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } priority_queue<pair<ll,int> > q,s; inline void dij(int x) { q.push(mk(-dis[x],x));vis[x]=1; while(q.size()) { int x=q.top().S;vis[x]=0; q.pop(); for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(dis[tn]>dis[x]+e[i]) { dis[tn]=dis[x]+e[i]; if(!vis[tn])q.push(mk(-dis[tn],tn)),vis[tn]=1; } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read();k=read(); for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } for(int i=1;i<=k;++i) { int x,y; x=read();y=read(); if(!v[x])v[x]=y; else v[x]=min(v[x],y); } for(int i=1;i<=n;++i) { dis[i]=INF; if(v[i])s.push(mk(-v[i],i)); } dis[1]=0; dij(1); while(s.size()) { int x=s.top().S; s.pop(); if(v[x]>=dis[x])continue; ++ans;dis[x]=v[x]; dij(x); } printf("%d\n",k-ans); return 0; }
T了意料之中這多是比spfa 更差的算法了...
能夠直接spfa 固然因爲spfa 已經死了。。因此直接就死了。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000000000ll #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define F first #define S second #define mk make_pair 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=100010,maxn=400010; int n,m,k,len; int ans; int v[MAXN],vis[MAXN]; int lin[MAXN],nex[maxn<<1],ver[maxn<<1],e[maxn<<1]; ll dis[MAXN]; inline void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } priority_queue<pair<ll,int> > s; queue<int>q; inline void dij(int x) { q.push(x);vis[x]=1; while(q.size()) { int x=q.front();vis[x]=0; q.pop(); for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(dis[tn]>dis[x]+e[i]) { dis[tn]=dis[x]+e[i]; if(!vis[tn])q.push(tn),vis[tn]=1; } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read();k=read(); for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } for(int i=1;i<=k;++i) { int x,y; x=read();y=read(); if(!v[x])v[x]=y; else v[x]=min(v[x],y); } for(int i=1;i<=n;++i) { dis[i]=INF; if(v[i])s.push(mk(-v[i],i)); } dis[1]=0; dij(1); while(s.size()) { int x=s.top().S; s.pop(); if(v[x]>=dis[x])continue; ++ans;dis[x]=v[x]; dij(x); } printf("%d\n",k-ans); return 0; }
考慮 一下正解 仔細觀察題目 有一個特殊的性質 鐵路都是和1 相連的邊 考慮所有都一塊兒跑最短路 那麼對於更新每一個點的dis以前咱們儘可能讓到i這個點的路徑儘可能長那麼 此時若是更新時大於2 那麼顯然 連向其的鐵路是沒有用的。
obviously 這種關係顯然不存在環 固然還有1條邊的狀況須要特判一下什麼的。這點小細節仍是很容易處理的。注意是雙向邊 坑了我好久好久。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000000000ll #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) 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=100010,maxn=400010; int n,m,k,len; int ans; int v[MAXN],vis[MAXN],pos[MAXN],pre[MAXN],sum[MAXN]; int lin[MAXN],nex[maxn<<1],ver[maxn<<1],e[maxn<<1]; ll dis[MAXN]; struct wy { int x; ll v; int friend operator <(wy a,wy b) { return a.v>b.v; } }; inline void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } priority_queue<wy> q; inline void dij() { while(q.size()) { int x=q.top().x; q.pop(); if(vis[x])continue; vis[x]=1; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(dis[tn]>dis[x]+e[i]) { dis[tn]=dis[x]+e[i]; pre[tn]=i;sum[tn]=sum[x]+1; q.push((wy){tn,dis[tn]}); } else if(dis[tn]==dis[x]+e[i]) { pre[tn]=min(pre[tn],i); sum[tn]=max(sum[tn],sum[x]+1); } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read();k=read(); for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } for(int i=1;i<=k;++i) { int x,y; x=read();y=read(); if(!v[x])v[x]=y; else v[x]=min(v[x],y); } for(int i=1;i<=n;++i) { dis[i]=INF; if(v[i]) { add(1,i,v[i]); pos[i]=len; } } dis[1]=0; q.push((wy){1,0}); dij(); for(int i=1;i<=n;++i) if(v[i]) { if(sum[i]>1)continue; if(pre[i]==pos[i])++ans; } printf("%d\n",k-ans); return 0; }
LINK:比較難的最短路問題 對於每一個點都須要求出那個等式的值一個比較顯然的思路發現是多源最短路floyd 而後n^2暴力枚舉統計答案便可。
考慮一下正解咱們 把i放到終點其實就是找j到i的最短路 把j放到隊列裏發現這道題就作完了qwq。。。我當時就沒想出來/kk
對於多源最短路 必然使用floyd更好一點 可是特殊的是本題中本身也能夠是終點,因此不難想到把終點放到隊列中找起點,恰好本身也能夠是終點因此直接跑最短路便可找到起點 那麼$dis_i$存的就是某個終點到起點的最短路辣。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000000000ll #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define mk make_pair 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 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=200010,maxn=200010; int n,m,k,len; int ans; int vis[MAXN]; int lin[MAXN],nex[maxn<<1],ver[maxn<<1]; ll dis[MAXN],e[maxn<<1]; priority_queue<pair<ll,int> > q; inline void add(int x,int y,ll z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline void dij() { while(q.size()) { int x=q.top().second; q.pop(); if(vis[x])continue; vis[x]=1; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(dis[tn]>dis[x]+e[i]) { dis[tn]=dis[x]+e[i]; q.push(mk(-dis[tn],tn)); } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;++i) { int x,y;ll z; x=read();y=read();z=read(); add(x,y,z<<1);add(y,x,z<<1); } for(int i=1;i<=n;++i) { dis[i]=read(); q.push(mk(-dis[i],i)); } dij(); for(int i=1;i<=n;++i)printf("%lld ",dis[i]); return 0; }
LINK:最小生成樹性質題目 這我拿頭都不必定能想出來 可持久化並茶几必定能寫,但看一下可否有更優的解法。
一個定理 設當前要加入的邊權爲z 那麼對於<=z-1的邊權 存在樹的 形態必定是固定的。該聯通的必定都聯通了 那麼對於邊權爲z的邊首要是判斷 在z-1的時候兩個端點x y是否聯通。
若是不連通呢?若是存在邊權和其相同的 呢?可能存在當前邊於前面加入的那些邊構成了環 這裏發現一條邊可能直接連通了兩個集合,另外一條邊聯通的 也是兩個集合 可是這兩個集合是相同的 若是咱們單單使用並茶几鏈接單點 並不能及時的判環,這裏 想了一下是連通塊直接相連 而後判斷便可 可是連事後還須要斷邊 因此 瞄了一眼題解 發現大多都是 按秩合併並茶几...(有路徑壓縮的 大霧) 值得一提的是 邊權不一樣的是不受影響的..
因此主要的性質是 就是上述定理辣。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define mod 1000000007 #define pii pair<int,int> #define mk make_pair using namespace std; inline int read() { int 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 int MAXN=500010; int n,m,maxx,Q,cnt; int f[MAXN],flag[MAXN],ans[MAXN],sz[MAXN],pos[MAXN]; struct wy { int x,y,z,id; }t[MAXN]; struct data { int x,y,id; int fa,son; }; vector<data>g[MAXN]; inline int cmp(wy a,wy b){return a.z<b.z;} inline int getfather(int x) { if(f[x]==x)return x; return getfather(f[x]); } inline void cut(int x,int y) { sz[x]-=sz[y]; f[y]=y;return; } int main() { freopen("1.in","r",stdin); n=read();m=read();cnt=1; for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read();z=read(); t[i]=(wy){x,y,z,i}; maxx=max(maxx,z); } sort(t+1,t+1+m,cmp); for(int i=1;i<=m;++i)pos[t[i].id]=i; for(int i=1;i<=n;++i)f[i]=i,sz[i]=1; Q=read(); for(int i=1;i<=Q;++i) { int k,x; k=read(); for(int j=1;j<=k;++j) { x=read(); g[t[pos[x]].z].push_back((data){t[pos[x]].x,t[pos[x]].y,i}); } } for(int i=1;i<=maxx;++i)//處理 前面的而後再加邊 { int last=-1; if(g[i].size()) { int ww=(int)g[i].size(); for(int j=0;j<ww;++j) { if(ans[g[i][j].id])continue; data w=g[i][j]; if(last==-1)last=j; int xx=getfather(w.x); int yy=getfather(w.y); if(xx==yy) { ans[w.id]=1; int p=j-1; while(p>=last) { cut(g[i][p].fa,g[i][p].son); --p; } last=-1; continue; } else { if(sz[xx]>sz[yy])swap(xx,yy); g[i][j].fa=yy;g[i][j].son=xx; sz[yy]+=sz[xx];f[xx]=yy; } if(j+1>ww||g[i][j+1].id!=w.id) { int p=j; while(p>=last) { cut(g[i][p].fa,g[i][p].son); --p; } last=-1; } } } while(cnt<=m&&t[cnt].z==i) { int xx=getfather(t[cnt].x); int yy=getfather(t[cnt].y); if(xx==yy){++cnt;continue;} if(sz[xx]>sz[yy])swap(xx,yy); f[xx]=yy;sz[yy]+=sz[xx]; ++cnt; } } for(int i=1;i<=Q;++i)puts(ans[i]?"NO":"YES"); return 0; }
LINK:不太會的dp題目1249Fmaximum weight subset 考試的時候寫到這道題的時候 就只剩下40分鐘了 仍是在外面下雨的 深夜 很不想寫 也沒有什麼思路 因此就咕了。
選出一些點集使這個點集之中任意兩點之間的距離不超過k 且 求點集的最大權。n 是200 顯然沒法狀壓和爆搜。仍是不會 咕咕咕...
看完題解丟臉的 爬過來填坑...我有一個狀態可是不太對頭 這道題中顯然是樹形dp 可是考慮到一些問題 關於深度咱們必然要記錄 否則很難搞這個限制.
還有一點值得一提的是 能夠設狀態了 f[i][j] 表示 以i爲根的子樹此時選擇點集深度任意一個點的深度知足>=j 且 該集合合法 時的最大值 。
那麼顯然有轉移 f[i][0]+=f[tn][k]...什麼的 仍是挺顯然的。可是存在漏洞 每次轉移咱們都是定點轉移也就是說==j的轉移 >j的那部分咱們不知道 可是 因爲單調性的緣故若是>j比==j更優那麼顯然能夠利用>j來更新。
讓f數組具備單調性便可。不須要換根 由於咱們沒有強制選根因此只要答案最優都是能夠任意一個點來獲得答案的。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define INF 1000000000 #define inf 1000000000 #define ll long long #define db double #define pb push_back #define un unsigned #define mod 1000000007 using namespace std; 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=210; int n,k,len; int f[MAXN][MAXN];//f[i][j] 表示 以i爲根此時選的點集的選的最淺的點的深度爲j 的最大價值 //f數組顯然具備單調性 即 f[x][j]>=f[x][j+1]; int v[MAXN]; int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1]; inline void add(int x,int y) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; } inline void dfs(int x,int father) { f[x][0]=v[x]; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(tn==father)continue; dfs(tn,x); f[x][0]+=f[tn][k]; } for(int i=1;i<n;++i)//枚舉狀態 { for(int w=lin[x];w;w=nex[w])//尋找轉移 { int tn=ver[w]; if(tn==father)continue; int cnt=f[tn][i-1]; for(int j=lin[x];j;j=nex[j]) { if(j==w)continue; int te=ver[j]; if(te==father)continue; cnt+=f[te][max(i-1,k-i)]; } f[x][i]=max(f[x][i],cnt); } } for(int i=n-1;i>=1;--i)f[x][i-1]=max(f[x][i-1],f[x][i]); } int main() { //freopen("1.in","r",stdin); n=read();k=read(); for(int i=1;i<=n;++i)v[i]=read(); for(int i=1;i<n;++i) { int x,y; x=read();y=read(); add(x,y);add(y,x); } dfs(1,0); printf("%d\n",f[1][0]); return 0; }
根據狀態的最優性 因此這是正確的 可是我以爲 這一類題仍是理解淺薄 qwq難受。
複雜度n^3 注意 這裏的確是n^3的 外層dfs 不計算到複雜度之中 時間複雜度最大n^3;
這裏之後將會記錄我每次CF比賽的 一些沒有作出來的題目的坑 和已經作出來的簡潔作法. 發現代碼簡潔 短才能在比賽中拿到一個很是好的名次.
當我看到這道題目的時候驚 到我了 由於我好像見過 還不會寫的那種 也沒多少時間了 關鍵是D題寫寫調調大約用了40分鐘 因此這道題也就成功GG.
如今 從新審視一下這道題目。解鎖人數一旦多了起來就能夠解鎖其餘人了 可是也能夠用錢來 解鎖求最少花費。
對於第一我的咱們必然是要用 錢來買的 可是咱們不必定要買 最小的那個 也多是適中的一個什麼的這關乎後續的選擇。
好像很難dp的樣子 貪心策略也沒有那麼的清晰 可是咱們能夠慢慢分析性質.
考慮一下 咱們把須要相同人數的 的放在一塊兒 咱們解鎖的時候必定是解鎖 這若干個隊列中某個的最小值(顯然
還有一個 性質咱們解鎖的時候除了花錢的那種 剩下的必然是 從人數從小到大的有序解鎖。
還有一個 能不花錢就不花錢 把能解鎖的都解鎖完了再考慮花錢。
還有一個性質 不必定花費最少的是必買的由於買了其可能還須要買下一個 可是花費高的卻能夠完成這樣的操做.
分析到這裏 咱們仍是沒有解決 第一我的要選擇誰的問題...可是不要放棄 我還能繼續挖掘性質.
還有一個性質 對於需求人數>n-1的咱們是必買的 且先買其不會使答案變得更差。可是題目中mi都是小於n的很遺憾...由此咱們得出了一個重要的結論 一我的要麼被買要麼被解鎖。
咱們可以推出的是要買人必定在解鎖以前進行這樣必定不會使得答案變得更差,,,固然咱們要跟據序列中的元素大小 來 進行一些決策..
考慮了一下dp 顯然咱們 作不了 由於還須要獲得人的集合 只能考慮貪心了qwq 煩躁 想不出來。
因而選擇看題解 這個貪心真的是精闢qwq 咱們不知道第一次要選哪一個人 也不能動態的解鎖 翻轉一個角度來看問題。
咱們儘量的解鎖 一些人 可是要解鎖一個 人 那麼必須保證其後面還有mi 我的咱們若是讓mi 從小到大的來選擇的話會出現一些小問題可能前面有的人已經選了 此時會對後面有影響。
在前面發生的衝突會影響到後面因此咱們倒着解決一些衝突 如何發現衝突?即每一個人咱們都想象成能夠免費 而後須要後面的人的人數爲 mi 可是設前面已經有 sum我的免費了。
那麼當前點的前面最多有n-mi我的免費 當sum>n-mi的時候此時必然起衝突且和後面的人無關此時咱們在前面取最小值便可。
能夠發現這樣知足貪心的最優性 真是神仙題目qwq.
//#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 1000000000000000ll #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double 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=200010; vector<ll>g[MAXN]; ll T,n,ans; priority_queue<ll,vector<ll>,greater<ll> > q; signed main() { //freopen("1.in","r",stdin); T=read(); while(T--) { n=read();ans=0; for(ll i=1;i<=n;++i) { ll x,y; x=read();y=read(); g[x].push_back(y); } //q.clear(); while(q.size())q.pop(); for(ll i=n-1;i>=0;--i) { for(unsigned j=0;j<g[i].size();++j) { q.push(g[i][j]); //cout<<g[i][j]<<endl; } while((ll)q.size()>n-i) { ans+=q.top(); q.pop(); } g[i].clear(); } printf("%lld\n",ans); } return 0; }
啊啊啊 每次都是最後一道題不會寫 都給我留下陰影了qwq。
哦不此次連C都不會了固然再考試結束的2min以後我才珊珊打完...靈光靈光 靈光。
此次是一個字符串翻轉問題。LINK:Equalizing Two Strings
對於兩個字符串咱們能夠隨便的進行翻轉 每次翻轉的大小必須相同詢問 通過若干次翻轉可否使兩個字符串等價。
//#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 min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double #define pii pair<ll,ll> #define mk make_pair #define mod 1000000007 #define ull unsigned long long using namespace std; 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 int MAXN=200010,maxn=30; int T; int n,flag,mark; char a[MAXN]; int b[MAXN],c[MAXN],vis[maxn]; int vis1[maxn],s[MAXN]; int main() { //freopen("1.in","r",stdin); T=read(); while(T--) { n=read();mark=flag=0; scanf("%s",a+1); memset(vis,0,sizeof(vis)); memset(vis1,0,sizeof(vis1)); for(int i=1;i<=n;++i) { int w=a[i]-'a'; ++vis[w]; b[i]=w+1; } scanf("%s",a+1); for(int i=1;i<=n;++i) { int w=a[i]-'a'; ++vis1[w]; s[i]=w+1; } for(int i=0;i<=25;++i)if(vis[i]!=vis1[i])flag=1; if(flag){puts("NO");continue;} for(int i=0;i<=25;++i)if(vis[i]>1)mark=1; if(mark){puts("YES");continue;} int cnt=0,cnt1=0; for(int i=1;i<=n;++i) for(int j=1;j<i;++j) if(b[j]>b[i])++cnt; for(int i=1;i<=n;++i) for(int j=1;j<i;++j) if(s[j]>s[i])++cnt1; cnt=cnt&1;cnt1=cnt1&1; if(cnt==cnt1){puts("YES");continue;} puts("NO"); } return 0; }