2020 icpc 沈阳

  1. Journey to Un’Goro 思维+dfs

大意:有个只包含‘r,b’的长度为 n 的字符串,对于区间 [l,r] 若区间内 ‘r’ 的个数是奇数,那么称这个区间为好的区间,现在需要构造字符串,使得它的好的区间是所有字符串中最多的,输出’'好的区间‘ ’的个数,以及按字典序从小到大输出所有这样的字符串,若超过100个只需要输出100个。

思路:对于区间问题,尤其是区间计数类问题,直接做不好做的话,不妨尝试转化成前缀问题。记 s u m i sum_i sumi 为 字符‘r’ 的个数的前缀和。那个对于区间 [l,r]中的 个数 即为 s u m r − s u m l − 1 sum_r-sum_{l-1} sumrsuml1,若使的个数为奇数,那么 s u m r sum_r sumr s u m l − 1 sum_{l-1} suml1 奇偶性不同。

s u m 0 、 s u m 1 、 . . . . . . . s u m n sum_0、sum_1、.......sum_n sum0sum1.......sumn 假设有 x个是奇数个,y 个是偶数个 x + y = n + 1 x+y=n+1 x+y=n+1

好的区间的个数=xy,根据基本不等式 当 x=y,xy 取最大值

也就是说 ∣ x − y ∣ < = 1 |x-y|<=1 xy<=1 又有 x+y =n+1。所以 x和y 均不会超过 (n+1)/2(上取整)。然后就可以dfs+剪枝写了。

代码如下:

#include <bits/stdc++.h>
#define rep(i,bbb,eee) for(int i=bbb;i<=eee;i++)
#define frep(i,bbb,eee) for(int i=bbb;i>=eee;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
#define pb push_back
#define AC signed
// #define x first
// #define y second
#define int long long 
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=100010,M=1000000007;
int n,lim,cnt,ans;
char s[N];
void dfs(int id,int cnt1,int cnt2,int sum)
{
	if(cnt1>lim||cnt2>lim)return ;
	if(id==n)
	{
		cout<<s<<"\n";
		cnt++;
		if(cnt==100)exit(0);
		return ;
	}
	s[id]='b';
	dfs(id+1,cnt1+(sum%2==0),cnt2+(sum%2),sum);
	s[id]='r';
	dfs(id+1,cnt1+(sum%2!=0),cnt2+(sum%2==0),sum+1);
}
void solve()
{
	cin>>n;
	int x=(n+1)/2,y=n+1-x;
	ans=x*y,lim=(n+2)/2;
	cout<<ans<<"\n";
	dfs(0,1,0,0);
}
AC main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int _=1;
	//cin>>_;
	while(_--)solve();
	return 0;
}
  1. Kobolds and Catacombs 签到

思路:直接模拟就好,从后往前预处理后缀最小值,然后从前往后扫一遍,并维护前缀最大值,若后缀最小值比当前前缀最大值小,那么必定得把他们分成一组。

  1. The Witchwood 签到
  2. The Boomsday Project dp

大意:给定 n,m,r 代表 n 种租车折扣卡,m个组成订单,以及不用折扣卡租车的花费。每种折扣卡有个时间限制,次数限制,和花费。求最终花费的最小值。

思路:不难看出要用dp 写,每种折扣卡既有时间限制,又有次数限制。直接状态定义的话很麻烦。我们注意到题目给出的条件:所有订单的租车次数之和是不超过3e5的,所以我们可以尝试变形下,用 a[i] 表示租第 i 次车的天数,从小到大排个序,将问题转化成线性dp。记租车的总数为 cnt。不难想出状态定义 f[i,j] 表示租前 i 次,且最后一次用了折扣卡 j 的最小花费。

接下来想想怎么状态转移。不妨令 b j b_j bj 表示第 j 种折扣卡当前左边界, d j d_j dj表示时间限制, c j c_j cj表示次数限制, v j v_j vj表示花费。

f [ i , j ] = m i n ( f [ i − 1 , j ] + r , f [ i − b [ j ] , j ] + v j ) f[i,j]=min(f[i-1,j]+r,f[i-b[j],j]+v_j) f[i,j]=min(f[i1,j]+r,f[ib[j],j]+vj)

j 都一样,显然可以省掉一维

其中 b j b_j bj 满足

a [ b j ] + d j − 1 > = a [ i ] a[b_j]+d_j-1>=a[i] a[bj]+dj1>=a[i]

b j + c j − 1 > = i b_j+c_j-1>=i bj+cj1>=i

我们知道 a i a_i ai 是单调不降的,所以 b j b_j bj 也是单调不降的。左边界越靠左折扣卡利用越充分,所以直接从上次的左边界开始判断就行了。就可以省掉一维从头开始枚举的时间复杂度,总的时间复杂度 O ( n ∗ c n t ) O(n*cnt) O(ncnt)

代码如下:

#include <bits/stdc++.h>
#define int long long 
#define rep(i,bbb,eee) for(int i=bbb;i<=eee;i++)
#define frep(i,bbb,eee) for(int i=bbb;i>=eee;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
#define pb push_back
#define AC signed
// #define x first
// #define y second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=300010,M=1000000007;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}
inline void print(int x)
{   
   if(x<0){putchar('-');x=-x;}
   if(x>9) print(x/10);
   putchar(x%10+'0');
}
int n,m,r,f[N],a[N],b[N],cnt;
struct node{
	int d,k,c;
}cd[510];
void solve()
{
	cin>>n>>m>>r;
	rep(i,1,n)
	{
		int d,k,c;
		cin>>d>>k>>c;
		cd[i]={d,k,c};
	}
	while(m--)
	{
		int p,q;
		cin>>p>>q;
		while(q--)a[++cnt]=p;
	}
	sort(a+1,a+1+cnt);

	rep(i,1,n)b[i]=1;

	rep(i,1,cnt)f[i]=1e18;

	rep(i,1,cnt)
	{
		f[i]=f[i-1]+r;
		rep(j,1,n)
		{
			while(a[b[j]]+cd[j].d<=a[i]||b[j]+cd[j].k<=i)b[j]++;
			f[i]=min(f[i],f[b[j]-1]+cd[j].c);
		}
	}
	cout<<f[cnt]<<"\n";

}
AC main()
{
	ios::sync_with_stdio(false);cin.tie(0);
	int _=1;
	//cin>>_;
	while(_--)solve();
	return 0;
}

总结:写题时对于某些细节可以先设出来,构思好大概再去想细节。

  1. Rise of Shadows 数论

不会数论,先鸽了

  1. United in Stormwind 阅读理解、模拟
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值