第一题很明显的差分约束……然而我居然TLE了???而且得了90分还是有运气成分的,下午再测时没有收改的程序导致把上午的题又被测了一遍,结果只得了80分……
所以为什么TLE呢……因为最后还加了一个二分……其实就是对差分约束的理解不够
因为
adde(i,i-1,0);
adde(i-1,i,1);
这么一个约束条件,所以整个图都是连通的,存在0~n的路径,不用二分加边!!
看来差分还需要(刷题?)再加强理解。
#include
#include
#include
using namespace std;
const int N=1000+5,M=10000+2000+5;
int to[M],w[M],nxt[M],head[N],etot;
int n,m;
void adde(int u,int v,int c){
to[++etot]=v;
nxt[etot]=head[u];
w[etot]=c;
head[u]=etot;
}
bool vis[N];
int SPFA(int st)
{
int in[N],dis[N];;bool exi[N];
memset(dis,32,sizeof(dis));
memset(exi,0,sizeof(exi));
memset(in,0,sizeof(in));
queue<int> q;
q.push(st);
vis[st]=1;dis[st]=0;
while(!q.empty()){
int u=q.front();q.pop();
exi[u]=0;
for(int i=head[u];i!=-1;i=nxt[i]){
int v=to[i];
vis[v]=1;
if(dis[v]>dis[u]+w[i]){
dis[v]=dis[u]+w[i];
in[v]++;
if(in[v]>n-1) return 0;
if(!exi[v]){
exi[v]=1;
q.push(v);
}
}
}
}
return -dis[0];
}
/*bool solve(int c)
{
memset(vis,0,sizeof(vis));
w[etot]=c;
for(int i=0;i<=n;i++)
if(!vis[i]) {
if(!SPFA(i)) return 0;
}
return 1;
}*/
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
memset(head,-1,sizeof(head));//可能会有0号点
scanf("%d%d",&n,&m);
while(m--){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
adde(r,l-1,-c);
}
for(int i=1;i<=n;i++){
adde(i,i-1,0);
adde(i-1,i,1);
}
printf("%d",SPFA(n));
/*//s[n]-s[0]<=Mid
//s[n]-s[0]<=inf 如果还是不行,说明这个条件本身就不成立 不过好像没有条件不成立的情况?
adde(0,n,0);
int l=0,r=n;
while(l>1;
if(solve(mid)) //没有负环,条件成立,可以更小
r=mid;//(l,r]
else l=mid;
}
printf("%d",r);*/
return 0;
}
T2想到了用并查集但是统计路径时有用了并查集,妥妥TLE……
那么正解就是——
在并查集时顺带更新路径。
del[]表示改点到其祖先节点的距离差。
最开始纠结了一阵子,在union(也就是把b所属集合加入a所属集合时)的时候要不要更改b集合中所有点的del[]呢?又怎么改所有点中的del[]呢?
然后发现,根本不用,只用更新fb的del值即可。因为在有关询问涉及到现b集合中的点时在getfa时del[b]会更新成正确值;如果没有涉及到b集合中的点那么del不会更新成正确值,反正用不到~
#include
#include
#include
using namespace std;
const int N=100000+5,M=100000+5;
int n,m;
int fa[N],del[N];//,dis[N];
//del[i] i与fa[i]的距离差
int getfa(int x)
{
if(fa[x]==x) return x;
else {
int fax=getfa(fa[x]);
del[x]+=del[fa[x]];
return fa[x]=fax;
}
}
void unionn(int pa,int pb,int c)
{
fa[pb]=pa;
del[pb]=c;
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) fa[i]=i;
while(m--){
char s[5];
scanf("%s",s);
if(s[0]=='!'){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
int pa=getfa(a),pb=getfa(b);
if(pa==pb) continue;
unionn(pa,pb,del[a]+c-del[b]);
}
else {
int a,b;
scanf("%d%d",&a,&b);
if(getfa(a)==getfa(b)){
printf("%d\n",del[b]-del[a]);
}
else printf("UNKNOWN\n");
}
}
return 0;
}
和火车调度的题面相同,但是数据范围十分BT
对于100%的数据,n,m<=100000,所有si的和不超过100000。
在这挂一下题解,因为我没有调这道题也应该不会调这道题了(进度紧啊没时间啊)
c
线段树优化建边的拓扑排序。注意到经停站把车站序列划分成了多个区间,每个区间对应O(log)个线段树上的节点,因此连边时可以把边数由O(nm)优化到O(m*log(n))。同时线段树的上下层节点也互相连边。具体细节可以参考标程。
假设共用了m杯酒,酒精含量分别为b1,b2…bm
a1+…+am=m*n
a1+…+am=n+n+…+n(m个)
(a1-n)+(a2-n)+…+(am-n)=0
设bi=ai-n,可以预处理出来。
我们要做的就是用尽量少的bi使bi之和=0
那么就可以想到bfs啦(也可以用bfs做)
因为数据范围
对于100%的数据,0<=n,a_i<=1000,1<=k<=100000。
|bi|<=1000
所以可以剪枝~也一定要剪枝不然妥妥TLE
#include
#include
#include
#include
using namespace std;
const int N=100000+5;
int n,k,a[N],b[N],dis[2000+5];bool exi[2005];
typedef pair<int,int> pii;
int bfs()
{
queue q;
q.push(make_pair(0,0));
while(!q.empty()){
pii x=q.front();q.pop();
for(int i=1;i<=k;i++){
if(!exi[x.first+b[i]+1000]&&abs(x.first+b[i])<=1000){
if(!(x.first+b[i])) return x.second+1;
exi[x.first+b[i]+1000]=1;
q.push(make_pair(x.first+b[i],x.second+1));
}
if(x.second==1000) return -1;//咦这个剪枝对不对?
}
}
return -1;
}
int main()
{
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++){
scanf("%d",&a[i]);
b[i]=a[i]-n;
}
printf("%d",bfs());
return 0;
}