• 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
    ",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
    ",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!!!!!
    }
    

      

  • 相关阅读:
    4.14打印特殊图案
    4.13十进制/二进制转换器
    4.12程序运行时间
    4.11 计算文件的大小
    4.10文件的读写
    4.9位运算
    CyclicBarrier
    tar 命令
    MySQL 常用函数介绍
    mysql 表转 java 实体 sql
  • 原文地址:https://www.cnblogs.com/Gloid/p/10503568.html
Copyright © 2020-2023  润新知