ACM日常训练日记——8.8(二分dp,最小生成树+克鲁斯卡尔算法和普利姆算法)
- codeforces训练
本题大意就是找到最后g不等于0的区间个数。
主要思路:找前缀和第一次大于k的下标idx(二分),然后我们发现idx+1的方案数相当于把idx+1当作左端点来算,然后我们就想到dp[i]代表以i为左端点的方案数。
总结:在比赛的时候找到了idx,但是因为没有想到用dp来表示,所以是一直递归下去,然后因为一些边界问题还是没有写出来。
ps:本题因为需要用到后面的状态,所以我们从后往前二分来做。
一般这种区间的问题,就是以左端点或者右端点为起点,然后在较小的时间复杂度内找到符合条件的区间数。
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};
#define int long long
typedef pair<int,int> pii;
const int N=200010;
int s[N];
int a[N];
int n,k;
int dp[N];
void solve()
{
cin>>n>>k;
memset(s,0,sizeof s);
memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
int ans=0;
for(int i=n;i>=1;i--)
{
int l=i-1,r=n+1;
while(l+1<r)
{
int mid=l+r>>1;
if(s[mid]-s[i-1]>k)
{
r=mid;
}else l=mid;
}
if(r==n+1)dp[i]+=n-i+1;
else dp[i]=dp[r+1]+r-i;
}
for(int i=1;i<=n;i++)ans+=dp[i];
cout<<ans<<endl;
}
signed main()
{
int good_luck_to_you;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// good_luck_to_you=1;
cin>>good_luck_to_you;
while(good_luck_to_you--)
{
solve();
}
system("pause");
}
- C. Arrow Path
DFS
// LUOGU_RID: 171384671
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MOD = 1e9 + 7;
const int MAX_N = 3e5 + 5;
int n;
bool vis[3][200005];
char mp[3][200005];
vector<int> g[MAX_N];
bool st[MAX_N];
int qpow(int a, int b, int m) {
int result = 1;
a %= m;
while (b > 0) {
if (b & 1) {
result = (result * a) % m;
}
a = (a * a) % m;
b >>= 1;
}
return result;
}
int xx[]={0,0,1,-1};
int yy[]={1,-1,0,0};
bool inmp(int x,int y){
return x>0&&x<3&&y>0&&y<n+1;
}
void dfs(int tx,int ty){
vis[tx][ty]=true;
if(tx==2 and ty==n){
return;
}
for(int i=0;i<4;i++){
int x=tx+xx[i];
int y=ty+yy[i];
if(inmp(x,y)&&!vis[x][y]){
vis[x][y]=true;
if(mp[x][y]=='>'&&y+1<=n&&!vis[x][y+1]){
dfs(x,y+1);
}
vis[x][y]=false;
if(mp[x][y]=='<'&&y-1>=1&&!vis[x][y-1]){
dfs(x,y-1);
}
vis[x][y]=false;
}
}
}
int32_t main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t=1;
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=2;i++){
for(int j=1;j<=n;j++){
cin>>mp[i][j];
}
}
memset(vis,false,sizeof(vis));
dfs(1,1);
if(vis[2][n])cout<<"YES"<<'\n';
else cout<<"NO"<<'\n';
}
return 0;
}
3.C. Naming Company
友谊赛的题,博弈论精彩,你需要去考虑每一次头尾的比较,字符串一是否大于字符串二所有的数,还有顺序,
我补题的时候思路对了,但是后面想把状态结合再一起的时候没有考虑周全,看了队长的代码后恍然大悟了属于是
我们可以开两个deque然后保留需要的数放进去,每次比较就可以直接用q1的front或者是back和q2的去比较,这样
就能每次比较全部。精彩,实在是精彩
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e8+10;
const int MOD = 1e9 + 7;
bool f[1008611];
int prime[1008611];
int cnt=0;
int ma(int a,int b,int c){
int t=max(a,b);
return max(t,c);
}
int32_t main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t=1;
// cin>>t;
while(t--){
string a,b;
cin>>a>>b;
int n=a.length();
sort(a.begin(),a.end());
sort(b.begin(),b.end());
int cnt=0;
if(n==1){
cout<<a.front();
continue;
}
deque<char> q1, q2;
for (int i = 0; i <(n+1)/2; ++i) {
q1.push_back(a[i]);
}
for (int i = 0; i <n/2 ; ++i) {
q2.push_front(b[n-i-1]);
}
int l=0;
int r = n - 1;
vector<char> ans(n);
for(int i=0;i<n;i++) {
if(q1.empty()){
ans[l++]=q2.front();
break;
}
if(q2.empty()){
ans[l++]=q1.front();
break;
}
if ((i & 1) == 0) {
if (q1.front() >= q2.back()) {
ans[r--]=q1.back();
q1.pop_back();
}else{
ans[l++]=q1.front();
q1.pop_front();
}
}else{
if (q2.back() <= q1.front()) {
ans[r--] = q2.front();
q2.pop_front();
} else {
ans[l++] = q2.back();
q2.pop_back();
}
}
}
for (auto i: ans) {
cout << i;
}
}
return 0;
}
-
nowcoder训练
1.挖沟
最小生成数的板子题
克鲁斯卡尔算法和普利姆算法
最小生成树模板题。
prim算法:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2*500000+10;
const int maxnn = 100000+10;
struct sy{
int to;
int next;
int w;
} edge[maxn];
int head[maxn];
struct Node{
int number;
int w;
bool operator < (const Node &n) const {
return w>n.w;
}
};
priority_queue<Node> pq;
bool vis[maxnn];
int cnt = 0;
int n, m;
void add_edge(int x, int y, int w)
{
edge[++cnt].next = head[x];
edge[cnt].to = y;
edge[cnt].w = w;
head[x] = cnt;
}
//普利姆算法,利用贪心的原理求最小生成树
int prim()
{
int ans = 0;
pq.push({1, 0});
while (pq.size())
{
Node node = pq.top();
pq.pop();
int number = node.number;
int w = node.w;
if (vis[number]) continue;
ans += w;
vis[number] = true;
//遍历这个点的其他边,找出没有遍历过的加入
for (int i=head[number];i;i = edge[i].next)
{
int next = edge[i].to;
// cout<<next<<" "<<edge[i].w<<endl;
if (vis[next]) continue;
pq.push({next, edge[i].w});
}
}
return ans;
}
int main()
{
int x, y ,w;
cin>>n>>m;
for (int i=1;i<=m;i++)
{
cin>>x>>y>>w;
add_edge(x, y, w);
add_edge(y, x, w);
}
int ans = prim();
cout<<ans;
return 0;
}
克鲁斯卡尔算法:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2*500000+10;
struct Node{
int x, y, w;
} node[maxn];
bool comp(Node n1, Node n2)
{
return n1.w<n2.w;
}
int fa[100000+10];
int n, m;
int find(int x)
{
return fa[x]==0?x:fa[x] = find(fa[x]);
}
int main()
{
cin>>n>>m;
for (int i=1;i<=m;i++)
{
cin>>node[i].x>>node[i].y>>node[i].w;
}
sort(node+1, node+1+m, comp);
int ans = 0;
for (int i=1;i<=m;i++)
{
int x = node[i].x;
int y = node[i].y;
int rootx = find(x);
int rooty = find(y);
if (rootx==rooty) continue;
ans += node[i].w;
fa[rootx] = rooty;
}
cout<<ans<<endl;
return 0;
}
2.道路建设
还是最小生成树的板子
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005,maxm=100005;
struct E{
int from,next,to,dis;
}edge[maxm*2];
int c,n,m,u,v,w;
int head[maxn],cnt=0,fa[maxn];
int find(int x){
if(fa[x]==x) return x;
else return fa[x]=find(fa[x]);
}
void unite(int x,int y){
fa[find(x)]=find(y);
}
void addedge(int from,int to,int dis){
edge[++cnt].next=head[from];
edge[cnt].from=from;
edge[cnt].to=to;
edge[cnt].dis=dis;
head[from]=cnt;
}
bool cmp(E a,E b){
return a.dis<b.dis;
}
int main(){
scanf("%d%d%d",&c,&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
int tot=0,sm=0;
for(int i=1;i<=m;i++) fa[i]=i;
sort(edge+1,edge+1+cnt,cmp);
for(int i=1;i<=cnt;i++){
if(find(edge[i].to)!=find(edge[i].from)){
sm+=edge[i].dis;
unite(edge[i].to,edge[i].from);
tot++;
}
if(tot==m-1) break;
}
if(sm<=c) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}