Codeforces Round #431 Div. 1

  A:显然每种字符的代价互不相关,dp并打表可得合并i个字符的最小代价是i*(i-1)/2。然后直接贪心分配每个字符即可。因为每次分配都将剩余代价降到了根号级别所以字符数量是足够的。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100010
char getc(){char c=getchar();while ((c<‘A‘||c>‘Z‘)&&(c<‘a‘||c>‘z‘)&&(c<‘0‘||c>‘9‘)) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1;c=getchar();}
	while (c>=‘0‘&&c<=‘9‘) x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n;
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read()*2;if (n==0) {cout<<‘a‘;return 0;}
	for (int i=0;i<26;i++)
	{
		for (int j=n;j>=2;j--)
		if (1ll*j*(j-1)<=n)
		{
			for (int k=1;k<=j;k++) putchar(‘a‘+i);
			n-=1ll*j*(j-1);
			break;
		}
		if (n==0) break;
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

  B:先找出每个会撞在一起的集合,然后可以发现相当于是在网格图中向某方向走遇到一个格点就拐个弯,讨论一下即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100010
char getc(){char c=getchar();while ((c<‘A‘||c>‘Z‘)&&(c<‘a‘||c>‘z‘)&&(c<‘0‘||c>‘9‘)) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1;c=getchar();}
	while (c>=‘0‘&&c<=‘9‘) x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,w,h,ansx[N],ansy[N];
struct data
{
	int op,x,t,i;
	bool operator <(const data&a) const
	{
		return x-t<a.x-a.t;
	}
}a[N];
bool cmp(const data&a,const data&b)
{
	return a.op<b.op||a.op==b.op&&a.x<b.x;
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read(),w=read(),h=read();
	for (int i=1;i<=n;i++) a[i].op=read(),a[i].x=read(),a[i].t=read(),a[i].i=i;
	sort(a+1,a+n+1);
	for (int i=1;i<=n;i++)
	{
		int t=i;
		while (t<n&&a[t+1].x-a[t+1].t==a[i].x-a[i].t) t++;
		sort(a+i,a+t+1,cmp);
		int x=0,y=0;
		for (int j=i;j<=t;j++) if (a[j].op==1) x++;else y++;
		for (int j=i;j<i+x;j++)
		{
			int W=i+x-j-1,H=y,X=min(W,H);
			if (W>=H) ansx[a[j].i]=a[j+X].x,ansy[a[j].i]=h;
			else ansx[a[j].i]=w,ansy[a[j].i]=a[i+x+X].x;
		}
		for (int j=i+x;j<=t;j++)
		{
			int W=t-j,H=x,X=min(W,H);
			if (W>=H) ansx[a[j].i]=w,ansy[a[j].i]=a[j+X].x;
			else ansx[a[j].i]=a[i+X].x,ansy[a[j].i]=h;
		}
		i=t;
	}
	for (int i=1;i<=n;i++) printf("%d %d\n",ansx[i],ansy[i]);
	return 0;
	//NOTICE LONG LONG!!!!!
}

  C:显然相当于求区间相邻两相同数的位置差之和。于是可以看成一个二维查询,即r前缀中所有前驱>=l的位置的权值和。可以树状数组套treap动态维护。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
#define ll long long
#define N 100010
#define lson tree[k].ch[0]
#define rson tree[k].ch[1]
char getc(){char c=getchar();while ((c<‘A‘||c>‘Z‘)&&(c<‘a‘||c>‘z‘)&&(c<‘0‘||c>‘9‘)) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1;c=getchar();}
	while (c>=‘0‘&&c<=‘9‘) x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,m,a[N],root[N],cnt;
set<int> pos[N];
struct data{int ch[2],p,x,v;ll s;
}tree[N<<5];
int pre(int p,int x)
{
	auto it=pos[x].find(p);it--;return *it;
}
void up(int k){tree[k].s=tree[lson].s+tree[rson].s+tree[k].v;}
void move(int &k,int p)
{
	int t=tree[k].ch[p];
	tree[k].ch[p]=tree[t].ch[!p],tree[t].ch[!p]=k,up(k),up(t),k=t;
}
void ins(int &k,int x,int v)
{
	if (k==0) {k=++cnt;tree[k].p=rand();tree[k].x=x;tree[k].s=tree[k].v=v;return;}
	tree[k].s+=v;
	if (tree[k].x<x) {ins(rson,x,v);if (tree[rson].p>tree[k].p) move(k,1);}
	else {ins(lson,x,v);if (tree[lson].p>tree[k].p) move(k,0);}
}
void del(int &k,int x,int v)
{
	if (tree[k].x==x) tree[k].v-=v;
	else if (tree[k].x<x) del(rson,x,v);
	else del(lson,x,v);
	up(k);
}
ll query(int k,int x)
{
	if (k==0) return 0;
	if (tree[k].x<x) return query(rson,x);
	else return tree[rson].s+tree[k].v+query(lson,x);
}
void Insert(int k,int x){int y=k-x;while (k<=n) ins(root[k],x,y),k+=k&-k;}
void Delete(int k,int x){int y=k-x;while (k<=n) del(root[k],x,y),k+=k&-k;}
ll Query(int k,int x){ll s=0;while (k) s+=query(root[k],x),k^=k&-k;return s;}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	srand(20020509);
	n=read(),m=read();
	for (int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<=n;i++) pos[i].insert(0);
	for (int i=1;i<=n;i++)
	{
		pos[a[i]].insert(i);
		Insert(i,pre(i,a[i]));
	}
	for (int i=1;i<=m;i++)
	{
		int op=read();
		if (op==1)
		{
			int p=read(),x=read(),y=pre(p,a[p]);
			if (a[p]==x) continue;
			auto it=pos[a[p]].find(p);it++;
			if (it!=pos[a[p]].end())
			{
				Delete(*it,p);
				Insert(*it,y);
			}
			pos[a[p]].erase(p);
			a[p]=x;
			pos[x].insert(p);
			int z=pre(p,x);
			it=pos[x].find(p);it++;
			if (it!=pos[x].end())
			{
				Delete(*it,z);
				Insert(*it,p);
			}
			Delete(p,y);Insert(p,z);
		}
		if (op==2)
		{
			int l=read(),r=read();
			printf("%I64d\n",Query(r,l));
		}
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

  D:

  搬上sol里的图,然后就变得很简单了。考虑初始边所衍生出的每个部分,这样就变成了一个子问题。大体类似于本质不同的有根树的计数,只是稍微复杂一点。

  设f[i][j]为操作i次后最小割为j的方案数。考虑先求出一个部分的方案数。即设g[i][j]为操作i次最小割为j(不考虑初始边)且钦定初始边只被操作一次的方案数,转移显然,这一部分是O(n4)的。

  然后考虑通过g推回f,由于相同的部分之间是无序的,类似上面的问题做一些操作即可。即设h[i][j][x][y]为操作i次后最小割为j且当前只考虑到(x,y)及其之前(如x‘<x||x‘==x&&y‘<y)的部分的方案数。转移时枚举(x,y)选择多少个,进行一个插板即可完成无序统计。复杂度是极小常数O(n5logn)。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define P 1000000007
#define N 52
char getc(){char c=getchar();while ((c<‘A‘||c>‘Z‘)&&(c<‘a‘||c>‘z‘)&&(c<‘0‘||c>‘9‘)) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1;c=getchar();}
	while (c>=‘0‘&&c<=‘9‘) x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,m,f[N][N],g[N][N],h[N][N][N][N],inv[N],ans;
void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
int C(int n,int m)
{
	if (m>n) return 0;
	int s=1;
	for (int i=n-m+1;i<=n;i++) s=1ll*s*i%P;
	return 1ll*s*inv[m]%P;
}
int F(int n,int m){return C(n+m-1,m);}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read(),m=read();int tmp=m;m=n+1;
	inv[0]=inv[1]=1;for (int i=2;i<=51;i++) inv[i]=P-1ll*(P/i)*inv[P%i]%P;
	for (int i=2;i<=50;i++) inv[i]=1ll*inv[i]*inv[i-1]%P;
	f[0][1]=1;g[0][0]=1;
	for (int x=0;x<=n;x++)
		for (int y=0;y<=m;y++)
		h[0][1][x][y]=1;
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++)
		{
			for (int x=0;x<i;x++)
			{
				for (int y=j+1;y<=m;y++)
				inc(g[i][j],1ll*f[x][y]*f[i-x-1][j]%P),
				inc(g[i][j],1ll*f[x][j]*f[i-x-1][y]%P);
				inc(g[i][j],1ll*f[x][j]*f[i-x-1][j]%P);
			}
		}
		for (int j=1;j<=m;j++)
		{
			for (int x=1;x<=n;x++)
				for (int y=1;y<=m;y++)
				{
					int ux=x,uy=y-1;if (uy==0) ux--,uy=m;
					h[i][j][x][y]=h[i][j][ux][uy];
					for (int k=1;k*x<=i&&k*y<j;k++)
					inc(h[i][j][x][y],1ll*h[i-x*k][j-y*k][ux][uy]*F(g[x][y],k)%P);
				}
			f[i][j]=h[i][j][n][m];
		}
	}
	m=tmp;
	cout<<f[n][m];
	return 0;
	//NOTICE LONG LONG!!!!!
}

  

原文地址:https://www.cnblogs.com/Gloid/p/10503568.html

时间: 2024-10-13 04:17:36

Codeforces Round #431 Div. 1的相关文章

【推导】【分类讨论】Codeforces Round #431 (Div. 1) B. Rooter&#39;s Song

给你一个这样的图,那些点是舞者,他们每个人会在原地待ti时间之后,以每秒1m的速度向前移动,到边界以后停止.只不过有时候会碰撞,碰撞之后的转向是这样哒: 让你输出每个人的停止位置坐标. ①将x轴上初始坐标记为(pi,0),y轴上的初始坐标记为(0,pi).只有pi-ti相同的才有可能发生碰撞.于是可以按照这一点将人划分为很多组,不同组之间绝对不会互相影响. ②假设一组内每个人都不会发生碰撞,那么所有的路线交叉点都是碰撞点.所以碰撞次数可能达到n^2次,暴力不可行. ③对于一组内,形成了一个网格图

Codeforces Round #431 (Div. 2)

A. Odds and Ends Where do odds begin, and where do they end? Where does hope emerge, and will they ever break? Given an integer sequence a1, a2, ..., an of length n. Decide whether it is possible to divide it into an odd number of non-empty subsegmen

Codeforces Round #431 (Div. 2) C

From beginning till end, this message has been waiting to be conveyed. For a given unordered multiset of n lowercase English letters ("multi" means that a letter may appear more than once), we treat all letters as strings of length 1, and repeat

Codeforces Round #431 (Div. 2) B

Connect the countless points with lines, till we reach the faraway yonder. There are n points on a coordinate plane, the i-th of which being (i, yi). Determine whether it's possible to draw two parallel and non-overlapping lines, such that every poin

Codeforces Round #431 (Div. 2) A

Where do odds begin, and where do they end? Where does hope emerge, and will they ever break? Given an integer sequence a1, a2, ..., an of length n. Decide whether it is possible to divide it into an odd number of non-empty subsegments, the each of w

【推导】【贪心】Codeforces Round #431 (Div. 1) A. From Y to Y

题意:让你构造一个只包含小写字母的可重集,每次可以取两个元素,将它们合并,合并的代价是这两个元素各自的从'a'到'z'出现的次数之积的和. 给你K,你构造的可重集必须满足将所有元素合而为一以后,所消耗的最小代价恰好为K. 考虑只包含一种类字母的消耗代价,以a为例: a 0 aa 1 aaa 3 aaa 6 aaaa 10 aaaaa 15 ... ... 而且如果再其上任意叠加别的字母的话,是互不干涉的.于是可以贪心地从K中依次减去最大的一个上表中的数,输出那么多'a',然后下一次换成'b',如

Codeforces Round #247 (Div. 2) ABC

Codeforces Round #247 (Div. 2) http://codeforces.com/contest/431 代码均已投放:https://github.com/illuz/WayToACM/tree/master/CodeForces/431 A - Black Square 题目地址 题意: Jury玩别踩白块,游戏中有四个区域,Jury点每个区域要消耗ai的卡路里,给出踩白块的序列,问要消耗多少卡路里. 分析: 模拟水题.. 代码: /* * Author: illuz

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i

Codeforces Round #424 (Div. 2) D. Office Keys(dp)

题目链接:Codeforces Round #424 (Div. 2) D. Office Keys 题意: 在一条轴上有n个人,和m个钥匙,门在s位置. 现在每个人走单位距离需要单位时间. 每个钥匙只能被一个人拿. 求全部的人拿到钥匙并且走到门的最短时间. 题解: 显然没有交叉的情况,因为如果交叉的话可能不是最优解. 然后考虑dp[i][j]表示第i个人拿了第j把钥匙,然后 dp[i][j]=max(val(i,j),min(dp[i-1][i-1~j]))   val(i,j)表示第i个人拿