noip53

T1

考场写了个很假的贪心,只有20pts。

正解:

把所有的 \(i\)\(f_{i}\) 连一条边,会形成个内向基环树森林。

先求出对于每个物品,得到最大的价值所对应的按钮,和次大的价值所对应的按钮。

求次大的原因是当你的图中出现环时,会有一个点不能被它最大的那个按钮给按完,会剩下1个,因为在这之前你按的那个按钮的商品就被按完了。

所以把这条最大的边,换成次大的边来替代即可。

每次跳最大价值所对应的按钮,跳到环上就换成次大的,答案减去对应的差值即可。

这个过程实际用循环就可以实现。

然而我老写假,所以还是用的dfs

Code
#include<cstdio>
#include<climits>
#define MAX 100003
#define INF INlatex
#define re reglatex
#define int64_latex
namespace somelatex
{
	struct stream	
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			int w=1; s=0; char ch=getchar();
			while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
			while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
			return s*=w,*this;
		}
	}cin;
	auto min = [](int a,int b) -> int { return a<b?a:b; };
}using namespace some;
namespace OMA
{
	int n;
	int64_t ans;
	struct node
	{ int f,c,d,a; }my[MAX];
	int val[MAX],xam1[MAX],xam2[MAX],nim;
	int cnt,cir[MAX];
	void dfs(int u)
	{
		if(cir[u]==cnt)
		{ ans -= nim; return ; }
		if(cir[u])
		{ return ; }
		cir[u] = cnt;
		//printf("u=%d\nbefore: %lld\n",u,ans);
		ans += 1ll*val[xam1[u]]*my[u].a;
		//printf("after: %lld\n",ans);
		nim = min(nim,val[xam1[u]]-val[xam2[u]]);
		if(xam1[u]!=u)
		{ dfs(xam1[u]); }
	}	
	auto main = []() -> signed
	{
		freopen("goods.in","r",stdin);
		freopen("goods.out","w",stdout);
		//freopen("my.out","w",stdout);
		cin >> n;
		for(re int i=1,f,c,d,a; i<=n; i++)
		{ my[i] = (node){(cin >> f,f),(cin >> c,c),(cin >> d,d),(cin >> a,a)}; }
		for(re int i=1; i<=n; i++)
		{
			val[i] = my[my[i].f].d-my[i].c;
			//printf("%d\n",val[i]);
			if(val[i]>val[xam1[my[i].f]])
			{ xam2[my[i].f] = xam1[my[i].f],xam1[my[i].f] = i; }
			else if(val[i]>val[xam2[my[i].f]])
			{ xam2[my[i].f] = i; }
		}
		for(re int i=1; i<=n; i++)
		{ if(!cir[i]){ nim = INF,cnt++; dfs(i); } /*printf("xam1=%d xam2=%d\n",xam1[i],xam2[i]);*/ }
		printf("%lld\n",ans);
		return 0;
	};
}
signed main()
{ return OMA::main(); }
/*
5
2 4 5 5
5 7 7 5
5 3 5 4
3 2 3 3
3 1 4 5
*/
/*
5
4 1 2 3
5 1 3 7
1 1 1 5
2 4 6 7
3 6 7 7
*/

T2

考场写了个假做法,极限是 \(O(n^{3})\) 的暴力,时间上没问题,但正确性是假的,然而仍有20pts。

正解:

首先答案串 \(p\) 一定是输入串的子串,因为最后一次插入。

先按照暴力来枚举答案串,通过长度+字符出现次数先判断一下是否合法。

然后检验,暴力删正着做20pts,反着做没试,但正反一块会T。

所以考虑按题解说的用dp来检验当前串是否合法。

\(dp_{i,j}\) 表示区间 \([i,j]\) 是否能合法。

如果 \(j-i+1\)\(len\) 的倍数,那么它就是一段合法的区间。

如果不是倍数,那么当多余的部分为 \(p\) 的前缀,其他的为若干段 \(p\) 时,该区间为合法区间。

转移时考虑第 \(j\) 个字符:

\(j\) 和之后的零散部分拼成一段,即\([i,j]\) 中有前缀:

\[dp_{i,j}|=dp_{i,j-1}\&[s_{j}=p_{(j-i)\mod len+1}] \]

\(j\) 是夹在零散部分之间的整块:

\[dp_{i,j}|=dp_{i,j-k\times len}\&dp_{j-k\times len+1,j} \]

记忆化一下就能跑的飞快。

Code
#include<map>
#include<vector>
#include<cstdio>
#include<cctype>
#include<cstring>
#define MAX 203
#define re register
using std::map;
using std::vector;
namespace some
{
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			bool w=0; s=0; char ch=getchar();
			while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
			while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
			return s=w?-s:s,*this;
		}
	}cin;
}using namespace some;
namespace OMA
{
	int t,len1;
	bool dp[MAX][MAX];
	char s[MAX],now[MAX];
	int cnt[26],tub[26][2];
	vector<char>ans;
	map<vector<char>,bool>vis;
	auto check = [](int len2) -> bool
	{
		for(re int i=1; i<=len1; i++)
		{ dp[i][i] = now[1]==s[i],dp[i][i] = 1; }
		for(re int len=2; len<=len1; len++)
		{
			char pre = now[(len-1)%len2+1];
			for(re int i=1,j; i<=len1-len+1; i++)
			{
				j = i+len-1,dp[i][j] = dp[i][j-1]&(s[j]==pre);
				for(re int k=1; k*len2<=len&&!dp[i][j]; k++)
				{ dp[i][j] |= dp[i][j-k*len2]&dp[j-k*len2+1][j]; }
			}
		}
		return dp[1][len1];
	};
	auto main = []() -> signed
	{
		freopen("string.in","r",stdin);
		freopen("string.out","w",stdout);
		cin >> t;
		while(t--)
		{
			scanf("%s",s+1); len1 = strlen(s+1);
			for(re int i=0; i<=25; i++)
			{ cnt[i] = 0; }
			for(re int i=1; i<=len1; i++)
			{ cnt[s[i]-'a']++; }
			//for(re int i=0; i<=25; i++)
			//{ printf("cnt[%c]=%d\n",i+'a',cnt[i]); }
			vis.clear(),ans.clear();
			bool flag,judge = false; int cot;
			for(re int len2=1; len2<=len1; len2++)
			{
				if(!(len1%len2))
				{
					//printf("len=%d\n",len2);
					cot = len1/len2,flag = true;
					for(re int i=0; i<=25; i++)
					{ if(cnt[i]%cot){ flag = false; break ; } tub[i][0] = cnt[i]/cot; }
					//printf("%d\n",len2);
					if(!flag)
					{ continue ; }
					//printf("%d\n",len2);
					for(re int i=1; i<=len1-len2+1; i++)
					{
						flag = true;
						for(re int j=0; j<=25; j++)
						{ tub[j][1] = tub[j][0]; }
						for(re int j=1; j<=len2; j++)
						{
							now[j] = s[i+j-1];
							if(--tub[now[j]-'a'][1]<0)
							{ flag = false; break ; }
						}
						if(!flag)
						{ continue ; }
						vector<char>tmp;
						for(re int j=1; j<=len2; j++)
						{ tmp.push_back(now[j]); }
						if(vis[tmp])
						{ continue ; }
						vis[tmp] = true;
						if(check(len2))
						{
							if(!judge)
							{ ans = tmp,judge = true; }
							else if(ans[0]-'a'>tmp[0]-'a')
							{ ans = tmp; }
						}
					}
					if(judge)
					{ break ; }
				}
			}
			//printf("\nans: ");
			for(re int i=0; i<ans.size(); i++)
			{ printf("%c",ans[i]); }
			printf("\n");
		}
		return 0;
	};
}
signed main()
{ return OMA::main(); }

T3

考场不会....

正解:

是根本没有想到的最短路....

先咕了.....

对于一条边 \(u,v,l,r\),如果 \(u\) 是没有学会的那个,那么最优的就应该是 \(u\)\(v\)\(l\) 吃了饭,\(v\)\(l+1\) 学会了毒瘤算法。

\(lim_{i}\) 表示只考虑-1的限制,\(i\) 最早学会毒瘤算法的时间。

先以所有最后没学会毒瘤算法的为源点,跑遍多源最短路,算出 \(lim\) 数组,那么对于 \(lim\) 的转移:

\[lim_{v}=l+1,lim_{u}>r\;and\;lim_{v}<l+1 \]

再以1为源点跑遍最短路,算出 \(dis\) 数组,表示在满足条件下 \(u\) 最早能学会的时间。

\(tmp=\max(l,\max(lim_{u},lim_{v}))\) ,则对于 \(dis\) 的转移:

\[dis_{v}=tmp,tmp\le r\;and\;dis_{v}>tmp \]

判断矛盾就枚举一下每个人,看是否有最后学会了的,但是其 \(dis\) 没有被更新的,还有一个比较草蛋的,注意一下1直接跟没有学会的恰饭,也是不合法的。

最后如果是有必要举行的午餐,答案则为 \(\max(l,\max(dis_{u},dis_{v}))\) ,不必要就随便输出一个 \([l,r]\) 之内的数即可,然而spj挂了,没必要的只有输出 \(l\) 才行。

Code
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#define MAX 200003
#define re register
const int INF = 1e9+3;
using std::queue;
namespace some
{
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			bool w=0; s=0; char ch=getchar();
			while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
			while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
			return s=w?-s:s,*this;
		}
	}cin;
	auto line = []() { printf("\n"); };
	auto debug = []() { printf("fuck\n"); };
	auto begin = []() { printf("begin\n"); }; auto end = []() { printf("end\n"); };
	auto max = [](int a,int b) -> int { return a>b?a:b; };
}using namespace some;
namespace SSSP
{
	struct graph
	{
		int next;
		int from;
		int to;
		int l,r;
	}edge[MAX<<1];
	int cnt=1,head[MAX];
	queue<int>q;
	bool vis[MAX];
	int lim[MAX],dis[MAX];
	auto add = [](int u,int v,int l,int r) -> void { edge[++cnt] = (graph){head[u],u,v,l,r},head[u] = cnt; };
	auto spfa1 = []() -> void
	{
		while(!q.empty())
		{
			//begin();
			int u = q.front(); q.pop(); vis[u] = false;
			//printf("u=%d\n",u);
			for(re int i=head[u],v,l,r; i; i=edge[i].next)
			{
				v = edge[i].to,l = edge[i].l,r = edge[i].r;
				if(lim[u]>r&&lim[v]<l+1)
				{
					//printf("v=%d\n",v);
					lim[v] = l+1;
					if(!vis[v])
					{ q.push(v); vis[v] = true; }
				}
			}
			//end();
		}
	};
	auto spfa2 = []() -> void
	{
		q.push(1); dis[1] = 0,vis[1] = true;
		while(!q.empty())
		{
			//debug(); begin();
			int u = q.front(); q.pop(); vis[u] = false;
			//printf("u=%d\n",u);
			for(re int i=head[u],v,l,r,tmp; i; i=edge[i].next)
			{
				v = edge[i].to,l = edge[i].l,r = edge[i].r;
				tmp = max(l,max(lim[v],dis[u]));
				if(tmp<=r&&dis[v]>tmp)
				{
					//printf("v=%d\n",v);
					dis[v] = tmp;
					if(!vis[v])
					{ q.push(v); vis[v] = true; }
				}
			}
			//end();
		}
	};
}using namespace SSSP;
namespace OMA
{
	int n,m,sta[MAX];
	auto main = []() -> signed
	{
		freopen("lunch.in","r",stdin); freopen("lunch.out","w",stdout);
		cin >> n >> m;
		for(re int i=1,u,v,l,r; i<=m; i++)
		{ cin >> u >> v >> l >> r; add(u,v,l,r),add(v,u,l,r); }
		for(re int i=1; i<=n; i++)
		{ cin >> sta[i]; dis[i] = INF; if(sta[i]==-1){ q.push(i); lim[i] = INF,vis[i] = true; } }
		spfa1(),spfa2();
		//begin(); for(re int i=1; i<=n; i++) { printf("%d\n",lim[i]); } end();
		//begin(); for(re int i=1; i<=n; i++) { printf("%d\n",dis[i]); } end();
		if(lim[1])
		{ printf("Impossible\n"); return 0; }
		for(re int i=1; i<=n; i++)
		{ if(sta[i]==1&&dis[i]>=INF){ printf("Impossible\n"); return 0; } }
		for(re int i=2; i<=cnt; i+=2)
		{
			if(dis[edge[i].from]>edge[i].r||dis[edge[i].to]>edge[i].r)
			{ printf("%d\n",edge[i].l); }
			else
			{ printf("%d\n",max(edge[i].l,max(dis[edge[i].from],dis[edge[i].to]))); }
		}
		return 0;
	};
}
signed main()
{ return OMA::main(); }

T4

考场不会....

正解:

我觉得rvalue学长写的挺好的....

Code
#include<cstdio>
#include<climits>
#include<cstring>
#define MAX 403
#define INF INT_MAX
#define re register
#define int long long
const int mod = 1e9+7;
namespace some
{
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			int w=1; s=0; char ch=getchar();
			while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
			while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
			return s*=w,*this;
		}
	}cin;
	auto abs = [](int a) -> int { return a>=0?a:-a; };
	auto max = [](int a,int b) -> int { return a>b?a:b; };
	auto min = [](int a,int b) -> int { return a<b?a:b; };
	auto swap = [](int &a,int &b) -> void { int t=a; a=b; b=t; };
	auto fd = [](int a,int b) -> int { return b-mod+a>=0?b-mod+a:a+b; };
}using namespace some;
namespace OMA
{
	int t,n,m;
	int l[MAX],r[MAX];
	int dp[MAX][MAX];
	auto main = []() -> signed
	{
		freopen("count.in","r",stdin);
		freopen("count.out","w",stdout);
		cin >> t;
		while(t--)
		{
			cin >> n >> m;
			memset(dp,0,sizeof(dp));
			for(re int u=1; u<=n; u++)
			{ l[u] = 0,r[u] = INF; }
			for(re int i=1,u,v; i<=m; i++)
			{
				cin >> u >> v;
				if(u<v)
				{ r[u] = min(r[u],v-u-1); }
				else
				{ l[v] = max(l[v],u-v); }
			}
			for(re int i=n; i; i--)
			{
				if(!l[i])
				{ dp[i][1] = 1; }
				for(re int j=l[i]+1; j<=n-i+1; j++)
				{
					int edge = min(r[i],j-1);
					if(!l[i])
					{ dp[i][j] = fd(dp[i][j],dp[i+1][j-1]); }
					if(edge==j-1)
					{ dp[i][j] = fd(dp[i][j],dp[i+1][j-1]); }
					for(re int k=l[i]; k<=edge; k++)
					{ dp[i][j] = fd(dp[i][j],dp[i+1][k]*dp[i+1+k][j-k-1]%mod); }
				}
			}
			printf("%lld\n",dp[1][n]);
		}
		return 0;
	};
}
signed main()
{ return OMA::main(); }
posted @ 2021-09-15 11:30  -OMA-  阅读(94)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end