T1
当天最难的题目..............
足足三页的题目描述..........
我们可以考虑到对于每一个串来说,串内部满足条件的数字可以先消去,所以我们直接先将串内的数消去,这样可以得到一个新的串
然后我们从两端向中间一个一个枚举数字,只要相等就继续枚举下去,如果说数量达到了k的话就取模并且更新原来的序列
最后就是每个循环剩余长度*(m-1)+仅去掉同一循环的剩余数的个数。(不懂的话自己推一下就好了.....这个公式很简单的.......)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
LL n,m,tot,k,s[100005][2],cnt;
int main()
{
freopen("guass.in","r",stdin);
freopen("guass.out","w",stdout);
std::scanf("%I64d%I64d%I64d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
LL x;
std::scanf("%I64d",&x);
if(!cnt||s[cnt][0]!=x)
{
s[++cnt][0]=x;
s[cnt][1]=1;
}
else s[cnt][1]++;
if(s[cnt][1]==k)s[cnt][1]=0,cnt--;
}
for(int i=1;i<=cnt;i++)
{
tot+=s[i][1];
}
int head=1;
int tail=cnt;
while(head<tail&&s[head][0]==s[tail][0])
{
if(!((s[head][1]+s[tail][1])%k))
{
head++;tail--;
}
else
{
s[head][1]=(s[head][1]+s[tail][1])%k;
s[tail][1]=0;
break;
}
}
LL ans=0;
if(head<tail)
{
for(int i=head;i<=tail;i++) ans+=s[i][1];
ans*=(m-1);
ans+=tot;
}
else if(head==tail)
{
if((s[head][1]*m)%k==0) ans=0;
else
{
ans=tot+s[head][1]*(m-1);
ans-=s[head][1]*m-s[head][1]*m%k;
}
}
std::cout<<ans;
}
T2
相对来说比较简单的一道题
但是还是有一些思维难度,但是想通了代码就出来了233333(考场上并没有想通于是写了50分算法)
然后发现最后50分输出p-1全部都能拿到,一句mmp脱口而出..........qaq想了那么久的骗分,结果他们前50分自己写,后面50直接o-1居然A了.......心态血崩...............
我们先看到m<=2的数据如何拿到分数
我们考虑到,由于可以向右和向下走,而不同路线中,每一条路径的长度是完全固定的,所以当只有两行的时候,其从左上到右下的分布一定是这个样子的........
好的.......我们可以发现,把第一行和第二行都记录下前缀和,然后直接枚举i就可以O(n)复杂度求出最优解
那三行的情况呢?
我们仍然按照刚才的思路
我们可以看到,这里的答案是s3+s2+s1
那么我们考虑到,直接转移的话复杂度是O(n^2),我们需要想一些办法让自己的程序变快
于是我们可以发现, 由于m==2给你暴力的方法已经是O(n)了出题人应该不会蠢到m==2和3方法想同,再加上1e5这么熟悉的数据范围..........
对!!nlog(n)的熟悉的感觉
我们现在在m=2的时候用相同的方法,首先枚举1、2行之间的分界线
得到s1+s2的和now
然后我们想要得到一个方法可以在O(log(n))的时候能够完成一个操作
使得我们在T3的剩余段内以log(n)的时间找到一个分界线,使得其和(s3+now)最大
那么我们该怎么做呢?
我们首先考虑到当now<p的时候,如果s3==p-now-1,那么now+p再%p就等于p-1.,这就是最大值
当now>p的时候,由于我们的结果是边加边取模的,所以now和s3都不会超过p,他们的和不超过2*p
那么在当前的段内取最大的值就好了
所以总结一下
我们需要维护一个数据结构,它支持两种操作
1、插入一个数字
2、在当前位置中查询一个数的位置
我们可以很轻松的联想到Splay。。。。。。。。。
事实是splay可以A
但是你数据结构学傻了................2333333
我们直接使用一个set,由于它可以自动排序
所以每一次将一个数字放进去就好了
完美.................
#include<cstdio>
#include<set>
#include<algorithm>
const int MAXN=1e5+5;
int sum[5][MAXN];
std::set<int>s;
int n,m,p;
int main()
{
std::freopen("candy.in", "r", stdin);
std::freopen("candy.out", "w", stdout);
std::scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
std::scanf("%d",sum[i]+j);
sum[i][j]+=sum[i][j-1]%=p;
}
}
if(m==1)
{
std::printf("%d\n",sum[1][n]);
return 0;
}
else
if(m==2)
{
int ans=-1;
for(int i=1;i<=n;i++)
{
ans=std::max(ans,(sum[1][i]%p+sum[2][n]%p-sum[2][i-1]%p+p)%p);
}
std::printf("%d\n",ans);
return 0;
}
else
if(m==3)
{
int ans=-1;
for(int i=n;i>=1;i--)
{
s.insert(((sum[3][n]-sum[3][i-1])%p-(sum[2][n]-sum[2][i]+p)%p+p)%p);
int now=(sum[2][n]%p-sum[2][i-1]%p+sum[1][i]+p)%p;
std::set<int>::iterator pos1=s.lower_bound(p-now);
if(pos1!=s.begin())
{
pos1--;
}
std::set<int>::iterator pos2=s.end();
if(pos2!=s.begin())
{
pos2--;
}
ans=std::max(ans,std::max((*pos1%p+now%p)%p,(*pos2%p+now%p)%p));
}
std::printf("%d\n ",ans);
}
return 0;
}
T3(家里电脑突然截不了图......只能贴手打23333333)
【问题描述】
小明和小刚正在玩如下的游戏。首先小明画一个有 N 个顶点,M 条边的有向图。然后
小刚试着摧毁它。在一次操作中他可以找到图中的一个点,并且删除它所有的入边或所有 的出边。
小明给每个点定义了两个值:Wi+和Wi-。如果小刚删除了第i个点所有的入边他要给 小明付 Wi+元,如果他删除了所有的出边就需要给小明付 Wi元。
找到小刚删除图中所有边需要的最小花费。
【输入格式】
从文件 game.in 中输入数据。
第一行有两个数 N,M(1<=N<=100,1<=M<=5000)。
第二行有 N 个整数,描述了 N 个点的 Wi+,同样的第三行是这 N 个点的 Wi-。所有的费用都是正数并且不超过 10^6。接下来的 M 行每行有两个数,代 表有向图中相应的一条边。
【输出格式】
输出到文件 game.out 中。
输出一行一个整数,即小刚的最小花费。
【样例输入】
3 6
1 2 3
4 2 1
1 2
1 1
3 2
1 2
3 1
2 3
【样例输出】
5
【样例说明】
样例的一个方案是:删除点 1,2 所有的入边,删除点 2 所有的出边。
【数据规模与约定】
对于 40% 的数据满足 1<=N<=10,1<=M<=20
对于 100% 的数据满足 1<=N<=100,1<=M<=5000
最大权闭合子图裸题目,我们可以发现,对于每一个点都删除其所有的入边和出边就能够将这个图的所有边全部删除,所以说我们会发现,我们只需要在这些所有删边方案里去最小的就好了
所以说:
1、我们先拆点,把每一个点拆成两个点
2、从超级源点src开始连边,向每一个点连边,其流量是删除这个点所有出边的流量(含义是删除这个点所有出边的花费,因为从源点连着,所以说只能删除出边,不能删除入边,因为没有入边连着,没有流量的流入)
2、对于每一个点,由这个点开始,向它和它能够到达的点( 拆点后)所有边链接一条费用为INF极大值的边,代表它可以流入向任意的点删除一条出边和那个点的入边并获得价值
3、对于每一个拆点后的点,向超级汇点sink连一条费用为删除其入边的费用,配合之前的INF的边,含义为删除入边的花费
4、跑一边最小割获得最小花费
5、最大流==最小割。
6、裸题
7、加当前弧优化(在最后15分钟的时候个人一直纠结要不要加当前弧优化,然后没有加,然后T了6个点,然后考完后加上了当前弧优化,然后每个点0.01s..........)
8、轻松A掉
#include<bits/stdc++.h>
using namespace std;
#define INF 1e9
struct node
{
int begin,end,value,next;
}edge[10410];
int cnt,Head[240],S,T,dis[240],q[240],cur[240];
void addedge(int bb,int ee,int vv)
{
edge[++cnt].begin=bb;edge[cnt].end=ee;edge[cnt].value=vv;edge[cnt].next=Head[bb];Head[bb]=cnt;
}
void addedge1(int bb,int ee,int vv)
{
addedge(bb,ee,vv);addedge(ee,bb,0);
}
int read()
{
int s=0,fh=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')fh=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+(ch-'0');ch=getchar();}
return s*fh;
}
int BFS()
{
int head,tail,u,v,i;
head=0;tail=1;q[tail]=S;
memset(dis,-1,sizeof(dis));dis[S]=0;
while(head!=tail)
{
head++;if(head==230)head=0;
u=q[head];
for(i=Head[u];i!=-1;i=edge[i].next)
{
v=edge[i].end;
if(edge[i].value>0&&dis[v]<0)
{
dis[v]=dis[u]+1;
tail++;if(tail==230)tail=0;
q[tail]=v;
}
}
}
if(dis[T]<=0)return 0;
else return 1;
}
int DFS(int u,int minflow)
{
int used=0,ans=0,i,v;
if(u==T)return minflow;
for(i=cur[u];i!=-1;i=edge[i].next)
{
v=edge[i].end;
if(edge[i].value>0&&dis[v]==dis[u]+1)
{
ans=minflow-used;
ans=DFS(v,min(ans,edge[i].value));
edge[i].value-=ans;
edge[i^1].value+=ans;
used+=ans;
if(minflow==used)return minflow;
}
}
if(used==0)dis[u]=-1;
return used;
}
int Dinic()
{
int maxflow=0,ans=0,i;
while(BFS()){for(i=1;i<=T;i++)cur[i]=Head[i];ans=DFS(S,INF);if(ans==0)break;maxflow+=ans;}
return maxflow;
}
int main()
{
//freopen("game.in","r",stdin);
//freopen("game.out","w",stdout);
int n,m,i,u,v,w1,w2;
n=read();m=read();
S=2*n+1;T=S+1;
memset(Head,-1,sizeof(Head));cnt=1;
for(i=1;i<=n;i++){w1=read();addedge1(i+n,T,w1);}
for(i=1;i<=n;i++){w2=read();addedge1(S,i,w2);}
for(i=1;i<=m;i++){u=read();v=read();addedge1(u,v+n,INF);/*addedge(S,u,w1[u]);addedge(v+n,T,w2[v]);*/}
printf("%d",Dinic());
fclose(stdin);
fclose(stdout);
return 0;
}