• FOI冬令营 Day2


    T1、直径(diameter)

    传送门

    Code

    //2019/2/13 50pts
    #include<bits/stdc++.h>
    #define ll long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    inline 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<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    int k;
    namespace solve1
    {
    	inline void work()
    	{
    		printf("%d
    ",k+2);register int i;
    		puts("1 2 3");
    		for(i=1;i<=k;++i) printf("1 %d 2
    ",i+2);
    		return;
    	}
    }
    namespace solve2
    {
    	inline void work()
    	{
    		register int i;
    		for(i=1;i<=k&&i*(i-1)/2!=k;++i);
    		int n=i;
    		printf("%d
    ",n+1);
    		for(i=1;i<=n;++i) printf("1 %d 1
    ",i+1);
    	}
    }
    int main()
    {
    	freopen("diameter.in","r",stdin);
    	freopen("diameter.out","w",stdout);
    	k=read();
    	if(k<=2000) solve1::work();
    	else if((ll)(sqrt(k*8ll+1ll))*(ll)(sqrt(k*8ll+1ll))==k*8ll+1ll) solve2::work();
    	//else solve3::work();
    	return 0;
    }
    

    /*
    	构造一个节点拉出3条链,除了与该节点相邻的边(都为233)以外,其余都为0 
    	三条链长度分别为a、b、c,那么k=ab+ac+bc=(a+c)(b+c)-c^2
    	从小到大枚举c,将k+c^2分解质因数,直到找到这样合适的(a,b,c)即可。
    	由于sqrt(500w)≈2236<5000/2=2500,所以有很高概率存在这样合适的解,事实上跑一遍所有k发现确实如此。
    	2019/2/13 21:54~22:20
    */
    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    inline 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<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    int main()
    {
    	freopen("diameter.in", "r", stdin);
    	freopen("diameter.out", "w", stdout);
    	int k=read();register int a,b,c;
    	for(a=1;a<=4999;++a)for(b=1;a+b<=4999&&a*b<=k;++b)
    	if((k-a*b)%(a+b)==0&&a+b+(c=(k-a*b)/(a+b))<=4999)
    	{
    		printf("%d
    ",a+b+c+1);puts("1 2 233");puts("1 3 233");if(c) puts("1 4 233");
    		int cnt=4+(c>0);
    		for(;--a>0;) printf("2 %d 0
    ",cnt++);
    		for(;--b>0;) printf("3 %d 0
    ",cnt++);
    		for(;--c>0;) printf("4 %d 0
    ",cnt++);
    		return 0;
    	}
    }
    

    T2、定价(price)

    传送门

    Code

    /*
    	用set维护每行当前可以为1的位
    	维护一个栈表示当前的1,位数最高的1放在栈底,每次可能弹出栈顶的若干个1,并加上一个1
    	如何找到最高的不能继续为1的点?先计算出每一位的存活上限,用优先对列维护即可 
    	均摊复杂度可行 
    	什么?不写优先队列也能过,那就不写了算了 
    	2019/2/14  19:30~20:23
    */ 
    #include<bits/stdc++.h>
    #define ll long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define Gho
    namespace IO
    {
    	const int lim=(1<<20)+5;
    	char buf[lim+5],*S,*T;
    	inline char gc(){if(S==T){T=(S=buf)+fread(buf,1,lim,stdin);if(S==T)return EOF;}return *S++;}
    	inline int read()
    	{
    		int x;char ch;bool f;
    		for(f=0;(ch=gc())<'0'||ch>'9';f=ch=='-');
    		for(x=ch^'0';(ch=gc())>='0'&&ch<='9';x=(x<<1)+(x<<3)+(ch^'0'));
    		return f?-x:x;
    	}
    }
    #ifdef Gho
    using namespace IO;
    #else
    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<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    #endif
    #define MN 1005
    const int mod=1e9+7;
    int n,m,q;
    std::set<int> d[MN];
    std::set<int>::iterator it;
    int fpow(int M){int r=1,x=2;for(;M;M>>=1,x=1ll*x*x%mod)if(M&1)r=1ll*x*r%mod;return r;}
    int st[MN],top,sum[MN];
    inline void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
    inline void dec(int &x,int y){x+=mod-y;if(x>=mod)x-=mod;}
    int solve() 
    {
    	register int ret=0,i,j;top=0;st[0]=mod;
    	for(i=1;i<=n;++i) 
    	{
    		int fst=-1;
    		for(j=1;j<=top&&d[i].find(st[j])!=d[i].end();++j);
    		if(j<=top) fst=st[j],top=j-1; 
    		//注意,这里求最高位的复杂度是错误的,仅仅是偷懒罢了
    		for(;;--top)
    		{
    			it=d[i].lower_bound(fst+1);
    			if(it==d[i].end()) return -1;
    			if(*it>=st[top]) fst=st[top];
    			else{st[++top]=*it;sum[top]=sum[top-1];add(sum[top],fpow(*it));break;}
    		}
    		add(ret,sum[top]);
    	}
    	add(ret,mod);return ret;
    }
    int main()
    {
    	freopen("price.in","r",stdin);
    	freopen("price.out","w",stdout);
    	n=read();m=read();q=read();
    	while(q--)
    	{
    		if(read()==1)
    		{
    			int r=read(),c=m-read();
    			if(d[r].find(c)!=d[r].end()) d[r].erase(c);
    			else d[r].insert(c);
    		}
    		else printf("%d
    ",solve());
    	}
    	return 0;
    }
    

    T3、排序(sort)

    传送门

    Code

    //2019/2/13
    //10pts
    #include<bits/stdc++.h>
    #define ll long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    inline 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<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    #define MN 1000005
    int n,a[MN];
    ll cnt;
    namespace solve1
    {
    	inline void work()
    	{
    		int ct=0;
    		for(int i=1;i<=n;++i)
    		for(int j=i+1;j<=n&&ct<cnt;++j,++ct)
    		if(a[j]<a[i]) std::swap(a[i],a[j]);
    		for(int i=1;i<=n;++i) printf("%d ",a[i]);puts("");
    	}
    }
    namespace solve2
    {
    	inline void work()
    	{
    		int i,j,k;
    		for(i=1;i<n,cnt>(n-i);cnt-=(n-i),++i);
    		for(j=1;j<i;++j) printf("%d ",j);
    		printf("%d ",n-cnt);
    		for(j=n;j>n-cnt;--j) printf("%d ",j);
    		for(j=n-cnt-1;j>=i;--j) printf("%d ",j);
    	}
    }
    int main()
    {
    	freopen("sort.in","r",stdin);
    	freopen("sort.out","w",stdout);
    	n=read();cnt=read();
    	register int i,j=1;
    	for(int i=1;i<=n;++i) a[i]=read(),j=(a[i]!=n+1-i)?0:1;
    	
    	if(cnt<=1e7)
    	{
    		solve1::work();
    		return 0;	
    	}
    	if(j)
    	{
    		solve2::work();
    		return 0;
    	}
    	return 0;
    }
    
    /*
    	我们先处理出完整的k轮,剩下的暴力更新即可。
    	考虑如何求出前k轮后的a数组,首先前k个数肯定已经排号了序(1~k)
    	然后,我们第i轮是从i这个数,往前找一个递增的子序列(满足a_i=min(a_1,...,a_{i+1})),把它循环右移一位
    	每个数最后必定会停留在最后一次被选中时的下一个数上。
    	通过冥想可以发现,代码的实现方式恰好能得出最后的位置 
    	
    	比较优秀的思考:
    	考虑一个01序列,经过k轮操作后,把最前面的至多k个0移至开头
    	对于序列a,一开始只有1~k对应的位置为0,其它都是1 (如 53421 k=2 的情况:11100)
    	而经过k轮后,变为00111,前两个0分别对应原序列中的1,2
    	我们枚举k+1~n,每次把当前数变成0,比如:我们把3变成0,为10100
    	经过k轮后,它会变成00110,对比00111,我们发现,3最终会停留在下标5上(这个位置由1变成了0)
    	我们要做的,其实是维护前k个0的位置,并把当前数放在第k+1个0的位置上
    	依次把下标加入pq,每次弹出最大的那一个即可。 
    	 
    	2019/2/13
    	题解写于 2019/2/14 
    */
    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    inline ll read()
    {
    	ll 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<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    const int MN=1000005; 
    int n,a[MN],pos[MN];ll cnt;
    std::priority_queue<int>q; 
    int main()
    {
    	freopen("sort.in","r",stdin);
    	freopen("sort.out","w",stdout);
    	n=read();cnt=read();register int i,j;
    	for(i=1;i<=n;++i) a[i]=read(),pos[a[i]]=i;
    	for(i=1;i<=n&&cnt>n-i;cnt-=n-i,a[i]=i,++i);
    	for(j=1;j<i;++j) q.push(pos[j]);
    	if(i>1) for(j=i;j<=n;++j) if(q.top()>pos[j]) a[q.top()]=j,q.pop(),q.push(pos[j]);
    	for(j=i+1;j<=i+cnt;++j) if(a[j]<a[i]) std::swap(a[j],a[i]);
    	for(i=1;i<=n;++i) printf("%d ",a[i]);
    	return 0;
    } 
    

    Blog来自PaperCloud,未经允许,请勿转载,TKS!

  • 相关阅读:
    第十一周总结
    第十周总结
    实验报告(七)及第九周总结
    第六次实验及第八周总结
    第五实验报告及第七周总结
    第六周&实验四
    期末课程总结
    第十四周课程总结&实验报告(简单记事本的实现)
    第十三周课程总结
    第十二周总结
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/10380578.html
Copyright © 2020-2023  润新知