写省选的题目对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; }