NOIP2017

小凯的疑惑

Link
放一个比较简洁的证明。
不妨设\(b>a\)
设答案为\(c\)
因为\((a,b)=1\),所以有\(c\equiv ax(\mod b)(x\in[1,b))\)\(c=ax+by(x\in[1,b))\)
\(y\le0\),那么\(c\)显然是可以由\(a,b\)表示的。
因此若要\(c\)不能被表示,需要满足\(y<0\)
因此当\(x=b-1,y=-1\)时,最大的\(c=ab-a-b\)

#include<cstdio>
int main()
{
    int a,b;
    scanf("%d%d",&a,&b);
    printf("%lld",1ll*a*b-a-b);
}

时间复杂度

Link
开个栈模拟就好了。

#include<bits/stdc++.h>
using namespace std;
const int N=107;
int l,O,used[26],vis[26];
string str,code[N];
stack<int>stk;
int read(int &p,string s)
{
    int ans=0;
    while(s[p]<'0'||s[p]>'9'&&p<s.size()) { if(s[p]=='n') return ++p,10000000; ++p; }
    while(s[p]>='0'&&s[p]<='9') ans=ans*10+s[p]-'0',++p;
    return ans;
}
int getO(){int p=3;if(str[2]=='n') return read(p,str); else return 0;}
int calc()
{
    while(!stk.empty()) stk.pop();
    int ans=0,now=0,a,b,p,x,k=-1;
    memset(used,0,sizeof used),memset(vis,0,sizeof vis);
    for(int i=1;i<=l;++i)
    {
	if(code[i][0]=='F')
	{
	    
	    if(used[x=code[i][2]-'a']) return -1;
	    stk.push(x),used[x]=1,p=4,a=read(p,code[i]),b=read(p,code[i]);
	    if(b-a>1000) if(k==-1) ++now,ans=max(ans,now),vis[x]=1;
	    if(a>b) if(k==-1) k=x;
	}
	if(code[i][0]=='E')
	{
	    if(stk.empty()) return -1;
	    x=stk.top(),stk.pop(),used[x]=0;
	    if(k==x) k=-1;
	    if(vis[x]) vis[x]=0,--now;
	}
    }
    if(stk.size()) return -1;
    return ans;
}
int main()
{
    int T,ans,i;
    scanf("%d",&T);
    while(T--)
    {
	for(scanf("%d ",&l),getline(cin,str),O=getO(),i=1;i<=l;++i) getline(cin,code[i]);
	if((ans=calc())==-1) puts("ERR"); else if(ans==O) puts("Yes"); else puts("No");
    }
}

逛公园

Link
首先我们跑出从\(1\)出发的最短路\(d1\)和反图上从\(n\)出发的最短路\(dn\)
然后我们处理出长度不超过\(d1_n+k\)的最短路边集,给它拓扑排序。
如果存在环,那么这个环一定是一个\(0\)环,此时是无解的。
否则我们把它的拓扑序跑出来。
对于一条边\((u,v,w)\),如果我们走这条边,会让路径长度比最短路大\(d1_u+w-d1_v\)
那么我们设\(f_{i,j}\)表示走到第\(i\)个点,走过的路径长度是\(d1_i+j\)
从小到大枚举\(j\),按拓扑序转移一遍,再把\(d1_u+w-d1_v\neq0\)的所有边转移一遍即可。

#include<bits/stdc++.h>
#define pi pair<int,int>
#define pb push_back
using namespace std;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
const int N=100007;
vector<pi>E[N];priority_queue<pi>q;stack<int>Q;
struct edge{int u,v,w;}e[N<<1];
int n,m,k,P,cnt,Tvis,dis[2][N],vis[N],deg[N],pos[N],f[N][51];
void inc(int &a,int b){a+=b,a=a>=P? a-P:a;}
void clearedge(){for(int i=1;i<=n;++i)E[i].clear();}
void cleardeg(){memset(deg,0,n+1<<2);}
void add(int u,int v,int w){E[u].pb(pi(v,w));}
void dij(int S,int id)
{
    memset(dis[id],0x3f,n+1<<2),++Tvis,q.push(pi(dis[id][S]=0,S));int u;
    while(!q.empty())
    {
	u=q.top().second,q.pop();if(vis[u]==Tvis)continue;vis[u]=Tvis;
	for(auto [v,w]:E[u])if(dis[id][u]+w<dis[id][v])dis[id][v]=dis[id][u]+w,q.push(pi(-dis[id][v],v));
    }
}
void toposort()
{
    cnt=0;int u;Q.push(1);
    while(!Q.empty())
    {
	u=Q.top(),Q.pop(),pos[++cnt]=u;
	for(auto [v,w]:E[u])if(!(--deg[v]))Q.push(v);
    }
}
int main()
{
    int i,j,flg;
    for(int T=read();T;--T)
    {
	n=read(),m=read(),k=read(),P=read();
	for(i=1;i<=m;++i)e[i]=(edge){read(),read(),read()};
	clearedge();
	for(i=1;i<=m;++i)add(e[i].u,e[i].v,e[i].w);
	dij(1,0);
	clearedge();
	for(i=1;i<=m;++i)add(e[i].v,e[i].u,e[i].w);
	dij(n,1);
	clearedge(),cleardeg();
	for(i=1;i<=m;++i)if(dis[0][e[i].u]+dis[1][e[i].v]+e[i].w<=dis[0][n]+k&&dis[0][e[i].u]+e[i].w==dis[0][e[i].v])add(e[i].u,e[i].v,0),++deg[e[i].v];
	for(i=1;i<=m;++i)e[i].w=dis[0][e[i].u]+e[i].w-dis[0][e[i].v];
	toposort();
	flg=0;
	for(i=1;i<=n;++i)if(deg[i]){flg=1;break;}
	if(flg){puts("-1");continue;}
	memset(f,0,sizeof f),f[1][0]=1;
	for(i=0;i<=k;++i)
	{
	    for(j=1;j<=cnt;++j)for(auto [v,w]:E[pos[j]])inc(f[v][i],f[pos[j]][i]);
	    for(j=1;j<=m;++j)if(e[j].w&&i+e[j].w<=k)inc(f[e[j].v][i+e[j].w],f[e[j].u][i]);
	}
	for(i=flg=0;i<=k;++i)inc(flg,f[n][i]);
	printf("%d\n",flg);
    }
}

奶酪

Link
暴力枚举两个洞是否有交,同时并查集维护连通性。
最后看看有没有和下表面有交的洞与和上表面有交的洞在同一连通块。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){int x;scanf("%d",&x);return x;}
const int N=1001;
int fa[N],x[N],y[N],z[N],f1[N],f2[N];
int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
int check(int i,int j,int r){return 1ll*(x[i]-x[j])*(x[i]-x[j])+1ll*(y[i]-y[j])*(y[i]-y[j])+1ll*(z[i]-z[j])*(z[i]-z[j])<=1ll*r*r;}
main()
{
    int T=read();
    while(T--)
    {
	int n=read(),h=read(),r=read(),tot1=0,tot2=0,flag=0;
	for(int i=1;i<=n;++i)
	{
	    fa[i]=i,x[i]=read(),y[i]=read(),z[i]=read();
	    if(z[i]+r<0||z[i]-r>h){--i,--n;continue;}
	    if(z[i]+r>=h) f1[++tot1]=i;
	    if(z[i]<=r) f2[++tot2]=i;
	}
	for(int i=1,j,fx,fy;i<=n;++i)
	    for(j=1;j<=i;++j)
		if(check(i,j,r<<1))
		    fx=find(i),fy=find(j),(fx==fy? fx=fy:fa[fx]=fy);
	for(int i=1,j;i<=tot1;++i)
	    for(j=1;j<=tot2;++j)
		flag=find(f1[i])==find(f2[j])? 1:flag;
	puts(flag? "Yes":"No");
    }
}

宝藏

Link
首先这种题一看我们就知道可以爆搜。
prim一眼假了,但是加个SA也能过。
所以我们来写状压。
\(f_{i,j,S}\)表示起点到\(j\)距离为\(i\),我们现在从\(j\)开始挖通\(S\)的最小代价。
转移是显然的:枚举\(S\)的子集\(T\)\(T\)中的点\(k\),挖通\((k,j)\)这条边(如果存在)。
\(f_{i,j,S}=\min\limits_{k\in T\subseteq S}(f_{i,j,S\setminus T}+f_{k,j+1,T\setminus\{k\}}+(i+1)E_{j,k})\)

#include<bits/stdc++.h>
using namespace std;
const int N=12,inf=0x3f3f3f3f;
int read(){int x;cin>>x;return x;}
int min(int a,int b){return a<b? a:b;}
int low[1<<N],cnt[1<<N],f[N][N][1<<N],E[N][N];
int main()
{
    int n=read(),m=read(),i,j,k,u,v,w,t,S,T,U,ans;
    memset(E,0x3f,sizeof E),memset(f,0x3f,sizeof f),U=(1<<n)-1;
    for(i=1;i<=m;++i) u=read()-1,v=read()-1,w=read(),E[u][v]=E[v][u]=min(E[u][v],w);
    for(i=1;i<=U;++i) cnt[i]=cnt[i&i-1]+1;
    for(i=0;i<=n;++i) low[1<<i]=i;
    for(i=1;i<=U;++i) low[i]=low[i&-i];
    for(i=0;i<n;++i) f[n-1][i][0]=0;
    for(i=n-2;~i;--i)
	for(j=0;j<n;++j)
	    for(f[i][j][0]=0,S=1;S<=U;++S)
		if(~S&1<<j&&cnt[S]<=n-i-1)
		    for(T=S;T;T=T-1&S)
			if(f[i][j][S&~T]<f[i][j][S])
			    for(k=low[t=T];t;k=low[t=t^1<<k])
				if(E[j][k]^inf)
				    f[i][j][S]=min(f[i][j][S],f[i+1][k][T^1<<k]+f[i][j][S^T]+(i+1)*E[j][k]);
    for(ans=inf,i=0;i<n;++i) ans=min(ans,f[0][i][U^1<<i]);
    return !printf("%d",ans);
}

列队

Link
动态开点线段树。
再开个vector记录一下放到后面的数就行了。

#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[11],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
    void Put(char x){*oS++=x;if(oS==oT)Flush();}
    int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
    void write(ll x){int top=0;while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('\n');}
}
using namespace IO;
const int N=300007;
int n,m,lim,cnt,root[N],ls[N<<5],rs[N<<5],sum[N<<5];vector<ll>vec[N];
void update(int &p,int l,int r,int x)
{
    if(!p) p=++cnt; ++sum[p]; if(l==r) return ;
    x<=mid? update(ls[p],l,mid,x):update(rs[p],mid+1,r,x);
}
int query(int p,int l,int r,int x)
{
    if(l==r) return l;
    int t=mid-l+1-sum[ls[p]];
    return x<=t? query(ls[p],l,mid,x):query(rs[p],mid+1,r,x-t);
}
ll work1(int x,ll y)
{
    int pos=query(root[n+1],1,lim,x);update(root[n+1],1,lim,pos);
    ll ans=pos<=n? 1ll*pos*m:vec[n+1][pos-n-1];
    return vec[n+1].push_back(y?y:ans),ans;
}
ll work2(int x,int y)
{
    int pos=query(root[x],1,lim,y);update(root[x],1,lim,pos);
    ll ans=pos<m? (x-1ll)*m+pos:vec[x][pos-m];
    return vec[x].push_back(work1(x,ans)),ans;
}
int main()
{
    n=read(),m=read();int Q=read();lim=max(n,m)+Q;
    for(int x,y;Q;--Q) x=read(),y=read(),write(y==m? work1(x,0):work2(x,y));
    return Flush(),0;
}
posted @ 2019-11-13 21:06  Shiina_Mashiro  阅读(156)  评论(0编辑  收藏  举报