3.19省选模拟

T1

//大概就是给定温度
//使得消耗能量最多
//首先冰系要高于温度
//火系要低于温度
//那么考虑火系升序,冰系降序
//那么就相当于查询某一段前缀/后缀的能量的最小值乘二
//目前可以简单的想到双log直接二分选择,直接找函数交点,然后树状数组求
//那么考虑优化到单log,直接线段树上二分不好吗?
//由于最后的答案必然是一个战士的温度,那么可以离散化了
//那么只需要在树状数组上套个倍增就好了,因为是单峰的,故可以直接加
#include<bits/stdc++.h>
#define int long long
#define MAXN 2000005
using namespace std;
struct node
{
      int op,k,t,x,y;
}que[MAXN];
struct Tr
{
 int tr[MAXN],n;
 void init(int x)
{
       n=x;
}
 int lowbit(int x)
{
     return x&(-x);
}
 void Insert(int x,int y)
{
      while(x<=n)
  {
      tr[x]+=y,x+=lowbit(x);
  }
}
 int query(int x)
{
   int res=0;
   while(x)
{
res+=tr[x],x-=lowbit(x);
}
   return res;
}
 int get(int x)
{
     return tr[x];
}
}tr0,tr1;
int n,cnt,sum1,p[MAXN];
signed main()
{
     scanf("%lld",&n);
     for(int i=1;i<=n;i++)
    {
       scanf("%lld",&que[i].op);
       if(que[i].op==1)
  {
     scanf("%lld%lld%lld",&que[i].t,&que[i].x,&que[i].y);
         p[++cnt]=que[i].x;
      }
   else scanf("%lld",&que[i].k);
   //将询问离线
    }
     //离散化
 sort(p+1,p+cnt+1);
 cnt=unique(p+1,p+cnt+1)-p-1;
 for(int i=1;i<=n;i++)
{
   if(que[i].op==1)
{
que[i].x=lower_bound(p+1,p+cnt+1,que[i].x)-p;
}
}
 tr0.init(cnt),tr1.init(cnt);
 for (int i=1;i<=n;i++)
{
   if(que[i].op==1)
{
      if(!que[i].t)
         tr0.Insert(que[i].x,que[i].y);
      else
         tr1.Insert(que[i].x+1,que[i].y),sum1+=que[i].y;
      //新插入冰火人
  }
else
{
     int k=que[i].k;
     if (!que[k].t)
       tr0.Insert(que[k].x,-que[k].y);
     else
       tr1.Insert(que[k].x+1,-que[k].y),sum1-=que[k].y;
                 //删除冰火人
                 //由于一个查前缀,一个查后缀
 //直接都查前缀记一个sum就好了
  }
   int s0=0,s1=sum1,f1=0,f2=0,p1=0,p2=0;
   for(int i=20;i>=0;i--)
{
 //这里是树状数组二分
     int np=p1+(1<<i),ns0=s0+tr0.get(np),ns1=s1-tr1.get(np);
     if(np>cnt) continue;
     //判断越界
     if(ns0<ns1)
{
         p1=np;
         s0=ns0,s1=ns1;
    }
     //找最优点
 //这个是找函数交点
 //由于函数可能没有交点
 //那么我们其实就可以在两个可能值里面取一个较大的就好了
  }
   f1=s0,s0=0,s1=sum1;
   if(p1<cnt)
{
     f2=min(tr0.query(p1+1),sum1-tr1.query(p1+1));
     for(int i=20;i>=0;i--)
{
       int np=p2+(1<<i),ns0=s0+tr0.get(np),ns1=s1-tr1.get(np);
       if(np>cnt) continue;
       if(ns0<ns1)
{
          p2=np;
          s0=ns0,s1=ns1;
      }
else if(min(ns0,ns1)==f2)
{
         p2=np;
         s0=ns0,s1=ns1;
      }
    }
  }
   if (max(f1,f2) == 0)
     cout<<"Peace"<<endl;
   else if(f1>f2)
     cout<<p[p1]<<' '<<f1*2<<endl;
   else
     cout<<p[p2]<<' '<<f2*2<<endl;
}
 return 0;
}

T2

//第一问显然dp
//第二问直接s*n建图跑就完了(寄了)
//第三问直接s*n建图跑就完了(寄了)
//首先比较显然的拆点是必要的
//然后根据大小关系连边也是显然的
//那么不显然的是,怎么保证网络流长度的问题
//dp[i]=k的话,表示第i位开头的最长不下降子序列长度为k
//dp[i]=1连T,dp[i]=k连S
//为什么这么就能保证长度了?
//大概明白了,我一开始想的是暴力建k层
//这样的话点数太多了,那么我们对于dp[i]=k连接的话
//必然保证了第一个点,并且连t我们也保证了最后一个点
//那么这道题就很显然了
//只不过代码实现有些出入
//其实有了这种思想
//把所有以此点为结尾的值=1连s
//=len的连接t
//这样也从所有正确的开头开始
#include<bits/stdc++.h>
#define INF 1000000007
#define MAXN 2000005
#define MAXM 505
using namespace std;
struct Edge
{
   int u,v,next,f;
}G[MAXN];
int head[MAXN],tot=0;
int a[MAXM],dp[MAXM],n,len,s,t,ans;
void add(int u,int v,int f)
{
   G[tot].u=u;G[tot].v=v;G[tot].f=f;G[tot].next=head[u];head[u]=tot++;
   G[tot].u=v;G[tot].v=u;G[tot].f=0;G[tot].next=head[v];head[v]=tot++;
}
int dis[100*MAXM];
bool bfs(int s,int t)
{
   memset(dis,0,sizeof(dis));
   queue<int>q;
q.push(s);
dis[s]=1;
   while(!q.empty())
{
       int u=q.front();
q.pop();
       if(u==t)return 1;
       for(int i=head[u];i!=-1;i=G[i].next)
{
           int v=G[i].v,f=G[i].f;
           if(dis[v]==0&&f)q.push(v),dis[v]=dis[u]+1;
      }
  }
   return 0;
}
int dfs(int u,int maxf,int t)
{
   if (u==t)return maxf;
   int rat=0;
   for (int i=head[u];i!=-1&&rat<maxf;i=G[i].next)
{
       int v=G[i].v;
int f=G[i].f;
       if(dis[v]==dis[u]+1&&f)
{
           int Min=min(maxf-rat,f);
           f=dfs(v,Min,t);
           G[i].f-=f;G[i^1].f+=f;rat+=f;
      }
  }
   if (!rat)dis[u]=-1;
   return rat;
}
int main(){
   scanf("%d",&n);
   for(int i=1;i<=n;i++)
{
   scanf("%d",&a[i]);
dp[i]=1;
}
   for(int i=1;i<=n;i++)
  {
  for(int j=1;j<i;j++)
  {
     if(a[j]<=a[i])dp[i]=max(dp[i],dp[j]+1);
  }
  }
   
   for(int i=1;i<=n;i++)
{
len=max(len,dp[i]);
}
   printf("%d\n",len);
   s=0;t=2*n+1;
   memset(head,-1,sizeof(head));
   for(int i=1;i<=n;i++)
{
if(dp[i]==1) add(s,i,1);
}
   for(int i=1;i<=n;i++)
{
if(dp[i]==len)add(i+n,t,1);
}
   for(int i=1;i<=n;i++)
{
add(i,i+n,1);
}
   for(int i=1;i<=n;i++)
  {
  for(int j=1;j<i;j++)
  {
  if(a[j]<=a[i]&&dp[j]+1==dp[i])add(j+n,i,1);
  }
  }
   while(bfs(s,t))
{
ans+=dfs(s,INF,t);
}
printf("%d\n",ans);

   add(1,1+n,INF);
add(s,1,INF);
   if(dp[n]==len)
{
add(n,n*2,INF);
add(n*2,t,INF);
}
   while(bfs(s,t))
{
ans+=dfs(s,INF,t);
}
if(ans>=INF)
{
cout<<1;
return 0;
}
   printf("%d\n",ans);
   return 0;
}

T3

一开始思想是正确的,被俩人带偏了(语文表述有问题)

显然从下(高度低)往上(高度高)扫

大概就是考虑这一行的所有状态了,首先合并这一行的联通块,然后方案数合并的话应该很好说

合并出来的大联通块,在不选择最高的时候方案数直接相乘就好了,然后最后每个联通块加1就好了

最后相乘

#include<bits/stdc++.h>
using namespace std;
int n,m,f[4000005],fa[4000005],ans=1;char s[2005][2005];
int id(int x,int y){return m*(x-1)+y;}
int Find(int x)
{  
   if(fa[x]==x) return x;
   return fa[x]=Find(fa[x]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]+1);
}
for(int i=1;i<=n*m;i++)
{
fa[i]=i,f[i]=1;
}
for(int i=n-1;i>1;i--)
{
for(int j=2;j<=m-1;j++)
{
  if(s[i][j]=='.'&&s[i][j-1]=='.')
{
fa[Find(id(i,j))]=Find(id(i,j-1));
}
}
for(int j=2;j<=m-1;j++)
{
if(s[i][j]=='.'&&s[i+1][j]=='.')
{
if(Find(id(i,j))!=Find(id(i+1,j)))
{
  f[Find(id(i,j))]=1LL*f[Find(id(i,j))]*f[Find(id(i+1,j))]%1000000007,fa[Find(id(i+1,j))]=Find(id(i,j));
}
}
 
}
for(int j=2;j<=m-1;j++)
{
if(s[i][j]=='.'&&Find(id(i,j))==id(i,j))f[id(i,j)]++;
}
}
for(int i=n-1;i>1;i--)
{
for(int j=2;j<=m-1;j++)
{
if(s[i][j]=='.'&&id(i,j)==Find(id(i,j)))ans=1LL*ans*f[id(i,j)]%1000000007;
}
}
printf("%d\n",ans);
return 0;
}

 

 

神笔连坐制度被停了一天课,亲切问候主任,为主任的行为点赞!

 

posted @ 2022-03-19 22:32  Authentic_k  阅读(61)  评论(0编辑  收藏  举报