zoukankan      html  css  js  c++  java
  • NOIp2012题解

    题目链接:https://loj.ac/problems/search?keyword=NOIP2012

    D1T1

    暴力

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define eps 1e-8
    int n,m,ans[1010],op[1010],a[1010],b[1010];
    char s[1010],t[1010];
    
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
        while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
        return x*f;
    }
    
    int main()
    {
    	scanf("%s",s+1);
    	scanf("%s",t+1);
    	n=strlen(s+1);m=strlen(t+1);
    	rep(i,1,n) 
    	{
    		if ((s[i]>='a') && (s[i]<='z')) 
    		{
    			a[i]=s[i]-'a';
    		}
    		else
    		{
    			a[i]=s[i]-'A';
    		}
    	}
    	rep(i,1,m)
    	{
    		if ((t[i]>='a') && (t[i]<='z'))
    		{
    			b[i]=t[i]-'a';
    		}
    		else
    		{
    			b[i]=t[i]-'A';
    			op[i]=1;
    		}
    	}
    	rep(i,1,m)
    	{
    		int id=(i-1)%n+1;
    		ans[i]=(b[i]-a[id]+26)%26;
    	}
    	rep(i,1,m)
    		if (op[i]) putchar(ans[i]+'A');
    		else putchar(ans[i]+'a');
    	return 0;
    }
    

    D1T2

    记前面的人的左手边前缀和为(P),当前两个人的左手上的数字为(l_1,l_2),右手上的数字为(r_1,r_2),并且1在2前面时最大花费更少,那么有(max(frac{P}{r_1},frac{Pl_1}{r_2})leq max(frac{P}{r_2},frac{Pl_2}{r_1}))

    首先我们注意到(frac{P}{r_1}<frac{Pl_2}{r_1},frac{P}{r_2}<frac{Pl_1}{r_2}),因此我们只需要比较后面两个数的大小,可以得到(frac{l_1}{r_2}<frac{l_2}{r_1})(l_1r_1<l_2r_2)。于是我们只要将所有人按照(l_ir_i)的大小排序即可

    但是注意到最后的答案可能很大,于是就需要手写一个高精度

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define eps 1e-8
    int n,m;
    pii p[100100];
    bool cmp(pii x,pii y)
    {
    	return 1ll*x.fir*x.sec<1ll*y.fir*y.sec;
    }
    
    struct bigint{
    	int len,a[5100];
    	
    	void init()
    	{
    		memset(a,0,sizeof(a));len=1;
    	}
    	
    	void print()
    	{
    		per(i,len,1) printf("%d",a[i]);
    		puts("");
    	}
    };
    
    bigint operator *(bigint x,int y)
    {
    	bigint z;z.init();z.len=x.len;
    	rep(i,1,x.len) z.a[i]=x.a[i]*y;
    	rep(i,1,x.len)
    	{
    		z.a[i+1]+=z.a[i]/10;
    		z.a[i]=z.a[i]%10;
    	}
    	while (z.a[z.len+1]) 
    	{
    		z.len++;
    		z.a[z.len+1]+=z.a[z.len]/10;
    		z.a[z.len]=z.a[z.len]%10;
    	}
    	return z;
    }
    
    bigint operator /(bigint x,int y)
    {
    	bigint z;z.init();z.len=x.len;
    	ll now=0;
    	per(i,x.len,1)
    	{
    		now=now*10+x.a[i];
    		z.a[i]=now/y;
    		now%=y;
    	}
    	while ((!z.a[z.len]) && (z.len)) z.len--;
    	return z;
    }
    
    bool operator <(bigint x,bigint y)
    {
    	if (x.len<y.len) return 1;
    	if (x.len>y.len) return 0;
    	per(i,x.len,1)
    		if (x.a[i]>y.a[i]) return 0;
    		else if (x.a[i]<y.a[i]) return 1;
    	return 0;
    }
    
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
        while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
        return x*f;
    }
    
    int main()
    {
    	n=read();
    	p[0].fir=read();p[0].sec=read();
    	rep(i,1,n) {p[i].fir=read();p[i].sec=read();}
    	sort(p+1,p+1+n,cmp);
    	bigint now,ans;now.init();
    	now.a[1]=1;now.len=1;
    	now=now*p[0].fir;ans=now/p[1].sec;
    	rep(i,1,n)
    	{
    		bigint tmp=now/p[i].sec;
    		ans=max(ans,tmp);
    		now=now*p[i].fir;
    	}
    	ans.print();
    	return 0;
    }
    

    D1T3

    预处理出在每一个位置小A/B向后走一步到达的位置,使用set解决

    不难发现第一问和第二问是相同的,都是给一个起始点(s)和最长运动距离(x),求小A和小B所走过的路径

    这个可以使用倍增解决,记一轮操作表示小A和小B都走了一步。(to[i][j])表示从(i)出发走了(2^j)轮之后的位置,(disa[i][j])表示小A从(i)出发在(2^j)轮中所走过的距离,(disb[i][j])表示小B从(i)出发在(2^j)轮中所走过的距离。之后所有的询问倍增跳跳跳就可以了。注意处理最后一轮可能只有小A向前走的情况

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define eps 1e-8
    int n,m,to[100100][21],toa[100100],tob[100100];
    ll disa[200200][21],disb[100100][21],nowa=0,nowb=0;
    pii p[100100];
    set<pii> s;
    set<pii>::iterator it1,it2;
    vector<pii> tmp;
    
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
        while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
        return x*f;
    }
    
    bool cmp(pii x,pii y)
    {
    	return ((x.fir<y.fir) || ((x.fir==y.fir) && (p[x.sec].fir<p[y.sec].fir)));
    }
    
    void find(int id)
    {
    	it1=s.lower_bound(p[id]);
    	tmp.clear();
    	if (it1!=s.begin())
    	{
    		--it1;
    		tmp.push_back(make_pair(abs(it1->fir-p[id].fir),it1->sec));
    		if (it1!=s.begin())
    		{
    			--it1;
    			tmp.push_back(make_pair(abs(it1->fir-p[id].fir),it1->sec));
    		}
    	}
    	it1=s.lower_bound(p[id]);it1++;
    	if (it1!=s.end())
    	{
    		tmp.push_back(make_pair(abs(it1->fir-p[id].fir),it1->sec));
    		it1++;
    		if (it1!=s.end())
    		{
    			tmp.push_back(make_pair(abs(it1->fir-p[id].fir),it1->sec));
    		}
    	}
    	sort(tmp.begin(),tmp.end(),cmp);
    	int len=tmp.size();
    	tob[id]=tmp[0].sec;
    	if (len>1) toa[id]=tmp[1].sec;
    }
    
    void init()
    {
    	n=read();
    	rep(i,1,n) 
    	{
    		p[i].fir=read();p[i].sec=i;
    	}
    	per(i,n,1) 
    	{
    		s.insert(p[i]);
    		if (i!=n) 
    		{
    			find(i);
    			int ta=toa[i],tb=tob[ta];
    			if (ta) disa[i][0]=abs(p[ta].fir-p[i].fir);
    			if (tb) disb[i][0]=abs(p[tb].fir-p[ta].fir);
    			to[i][0]=tb;
    		}
    	}
    	per(i,n,1)
    	{
    		rep(j,1,20)
    		{
    			to[i][j]=to[to[i][j-1]][j-1];
    			disa[i][j]=disa[i][j-1]+disa[to[i][j-1]][j-1];
    			disb[i][j]=disb[i][j-1]+disb[to[i][j-1]][j-1];
    		}
    	}
    }
    
    void work(int st,int lim)
    {
    	int now=st;
    	per(i,20,0)
    	{
    		if ((to[now][i]) && (disa[now][i]+disb[now][i]<=lim))
    		{
    			lim-=(disa[now][i]+disb[now][i]);
    			nowa+=disa[now][i];nowb+=disb[now][i]; 
    			now=to[now][i];
    		}
    	}
    	if (toa[now])
    	{
    		if (lim>=disa[now][0])
    		{
    			lim-=disa[now][0];
    			nowa+=disa[now][0];
    		}
    	}
    }
    
    void solve1(int lim)
    {
    	int ans=0;ll ansa=0,ansb=0;
    	rep(st,1,n)
    	{
    		nowa=0;nowb=0;
    		work(st,lim);
    		if ((nowb) && ((!ans) || (nowa*ansb<nowb*ansa)))
    		{
    			ansa=nowa;ansb=nowb;ans=st;
    		}
    	}
    	printf("%d
    ",ans);
    }
    
    void solve2()
    {
    	m=read();
    	while (m--)
    	{
    		nowa=0;nowb=0;
    		int st=read(),lim=read();
    		work(st,lim);
    		printf("%lld %lld
    ",nowa,nowb);
    	}
    }
    
    int main()
    {
    	init();
    	solve1(read());
    	solve2();
    	return 0;
    }
    

    D2T1

    扩欧模板题

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define eps 1e-8
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
        while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
        return x*f;
    }
    
    void exgcd(ll a,ll b,ll &x,ll &y)
    {
    	if (b==0) {x=1;y=0;return;}
    	else
    	{
    		exgcd(b,a%b,x,y);
    		ll tmpx=x,tmpy=y;
    		x=tmpy;y=tmpx-a/b*tmpy;
    	}
    }
    
    int main()
    {
    	int a=read(),b=read();
    	ll x=0,y=0;exgcd(a,b,x,y);
    	x=(x%b+b)%b;
    	printf("%lld",x);
    	return 0;
    }
    

    D2T2

    反正我想到了两种做法

    1)二分答案+差分check

    2)线段树

    由于好久没写线段树了就写了棵线段树

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define eps 1e-8
    int n,m,seg[4004000],tag[4004000],a[4004000];
    
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
        while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
        return x*f;
    }
    
    void build(int id,int l,int r)
    {
    	if (l==r)
    	{
    		seg[id]=a[l];return;
    	}
    	int mid=(l+r)>>1;
    	build(id<<1,l,mid);build(id<<1|1,mid+1,r);
    	seg[id]=min(seg[id<<1],seg[id<<1|1]);
    }
    
    void pushdown(int id)
    {
    	if (tag[id])
    	{
    		seg[id<<1]-=tag[id];seg[id<<1|1]-=tag[id];
    		tag[id<<1]+=tag[id];tag[id<<1|1]+=tag[id];
    		tag[id]=0;
    	}
    }
    
    void modify(int id,int l,int r,int ql,int qr,int val)
    {
    	pushdown(id);
    	if ((l>=ql) && (r<=qr))
    	{
    		seg[id]-=val;tag[id]+=val;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if (ql<=mid) modify(id<<1,l,mid,ql,qr,val);
    	if (qr>mid) modify(id<<1|1,mid+1,r,ql,qr,val);
    	seg[id]=min(seg[id<<1],seg[id<<1|1]);
    }
    
    int query(int id,int l,int r,int ql,int qr)
    {
    	pushdown(id);
    	if ((l>=ql) && (r<=qr)) return seg[id];
    	int ans=maxd,mid=(l+r)>>1;
    	if (ql<=mid) ans=min(ans,query(id<<1,l,mid,ql,qr));
    	if (qr>mid) ans=min(ans,query(id<<1|1,mid+1,r,ql,qr));
    	return ans;
    }
    
    int main()
    {
    	n=read();m=read();
    	rep(i,1,n) a[i]=read();
    	build(1,1,n);
    	rep(i,1,m)
    	{
    		int d=read(),l=read(),r=read(),now=query(1,1,n,l,r);
    		//cout << now << endl;
    		if (now<d) {printf("-1
    %d",i);return 0;}
    		modify(1,1,n,l,r,d);
    	}
    	puts("0");
    	return 0;
    }
    

    D2T3

    问的东西其实是“军队最长移动时间的最小值”,于是我们考虑二分答案

    一个比较显然的贪心是:在可以的情况下,使得军队所在位置的(dep)最小是最优的,因为它一定会比它的儿子覆盖更多的叶子节点

    我们将所有的军队先尽可能的往上提,如果它到达不了(1)节点,那就让他在那个最远的位置定下来。对于那些能够到达(1)节点的军队,他们可以通过根节点来控制其他的子树,不难发现如果要这么做的话让军队驻扎在(1)的儿子节点一定是最优的

    找到所有的可以移动的军队后,将它们的剩余距离从小到大排序。同时将(1)的儿子节点中有叶子没有被控制的节点拎出来,也按照到(1)的距离从小到大排个序。之后贪心的匹配就好了。将军队往上提的过程可以使用倍增加速,最后的时间复杂度是(2)(log)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define eps 1e-8
    struct node{int to,nxt,cost;}sq[100100];
    int all=0,head[100100];
    int n,m,q,col[10010],fa[10010][16],dis[10010][16],dep[10010];
    struct edgenode{int u,v,w;}edge[50050];
    bool operator<(edgenode p,edgenode q) {return p.w>q.w;}
    
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
        while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
        return x*f;
    }
    
    void add(int u,int v,int w)
    {
    	all++;sq[all].to=v;sq[all].nxt=head[u];sq[all].cost=w;head[u]=all;
    }
    
    int find(int x) {if (col[x]==x) return col[x];col[x]=find(col[x]);return col[x];}
    
    void dfs(int u,int fu)
    {
    	dep[u]=dep[fu]+1;
    	rep(i,1,15) 
    	{
    		fa[u][i]=fa[fa[u][i-1]][i-1];
    		dis[u][i]=min(dis[u][i-1],dis[fa[u][i-1]][i-1]);
    	}
    	for (int i=head[u];i;i=sq[i].nxt)
    	{
    		int v=sq[i].to;
    		if (v==fu) continue;
    		fa[v][0]=u;dis[v][0]=sq[i].cost;
    		dfs(v,u);
    	}
    }
    
    int query(int u,int v)
    {
    	if (dep[u]<dep[v]) swap(u,v);
    	int tmp=dep[u]-dep[v],ans=maxd;
    	per(i,15,0)
    		if ((tmp>>i)&1) {ans=min(ans,dis[u][i]);u=fa[u][i];}
    	if (u==v) return ans;
    	per(i,15,0)
    		if (fa[u][i]!=fa[v][i])
    		{
    			ans=min(ans,min(dis[u][i],dis[v][i]));
    			u=fa[u][i];v=fa[v][i];
    		}
    	ans=min(ans,min(dis[u][0],dis[v][0]));
    	return ans;
    }
    
    int main()
    {
    	n=read();m=read();
    	rep(i,1,m)
    	{
    		edge[i].u=read();edge[i].v=read();edge[i].w=read();
    	}
    	sort(edge+1,edge+1+m);
    	rep(i,1,n) col[i]=i;
    	rep(i,1,m)
    	{
    		int u=edge[i].u,v=edge[i].v,w=edge[i].w;
    		int fx=find(u),fy=find(v);
    		if (fx!=fy)
    		{
    			add(u,v,w);add(v,u,w);
    			col[fx]=fy;
    		}
    	}
    	memset(dis,0x3f,sizeof(dis));
    	rep(i,1,n)
    		if (!dep[i]) dfs(i,0);
    	q=read();
    	while (q--)
    	{
    		int u=read(),v=read();
    		if (find(u)!=find(v)) {puts("-1");continue;}
    		printf("%d
    ",query(u,v));
    	}
    	return 0;
    }
    
  • 相关阅读:
    luogu P1833 樱花 看成混合背包
    luogu P1077 摆花 基础记数dp
    luogu P1095 守望者的逃离 经典dp
    Even Subset Sum Problem CodeForces
    Maximum White Subtree CodeForces
    Sleeping Schedule CodeForces
    Bombs CodeForces
    病毒侵袭持续中 HDU
    病毒侵袭 HDU
    Educational Codeforces Round 35 (Rated for Div. 2)
  • 原文地址:https://www.cnblogs.com/encodetalker/p/11711197.html
Copyright © 2011-2022 走看看