Codeforces Round #426 Div. 1

  A:考虑每个质因子,显然要求次数之和是3的倍数,且次数之差的两倍不小于较小的次数。对于第一个要求,乘起来看开三次方是否是整数即可。第二个取gcd,两个数分别除掉gcd,之后看两个数的剩余部分是否都能被gcd整除即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long

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;
ll cube(ll x)
{
	return x*x*x;
}
signed main()
{
	n=read();
	while (n--)
	{
		int x=read(),y=read();
		ll z=1ll*x*y,t=pow(z,1.0/3);
		if (cube(t-1)==z||cube(t)==z||cube(t+1)==z)
		{
			ll u=gcd(x,y);
			x/=u,y/=u;
			//cout<<x<<' '<<y<<' '<<u<<endl;
			if (u%x==0&&u%y==0) printf("Yes\n");
			else printf("No\n");
		}
		else printf("No\n");
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

  B:显然有f[i][j]表示前i位分成j段的最大价值。考虑套路,在每个数最后一次出现的位置标1。这样转移时要查的就是后缀和与dp值的和的最大值。容易发现线段树区间加查查最大值就完了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 35010
#define M 55
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][M],a[N],pre[N],p[N],L[N<<2],R[N<<2],tree[N<<2],lazy[N<<2];
void up(int k){tree[k]=max(tree[k<<1],tree[k<<1|1]);}
void build(int k,int l,int r,int x)
{
	L[k]=l,R[k]=r;lazy[k]=0;
	if (l==r) {tree[k]=f[l][x];return;}
	int mid=l+r>>1;
	build(k<<1,l,mid,x);
	build(k<<1|1,mid+1,r,x);
	up(k);
}
void update(int k,int x)
{
	tree[k]+=x;
	lazy[k]+=x;
}
void down(int k)
{
	update(k<<1,lazy[k]);
	update(k<<1|1,lazy[k]);
	lazy[k]=0;
}
void add(int k,int l,int r)
{
	if (L[k]==l&&R[k]==r) {tree[k]++,lazy[k]++;return;}
	if (lazy[k]) down(k);
	int mid=L[k]+R[k]>>1;
	if (r<=mid) add(k<<1,l,r);
	else if (l>mid) add(k<<1|1,l,r);
	else add(k<<1,l,mid),add(k<<1|1,mid+1,r);
	up(k);
}
int query(int k,int l,int r)
{
	if (L[k]==l&&R[k]==r) return tree[k];
	if (lazy[k]) down(k);
	int mid=L[k]+R[k]>>1;
	if (r<=mid) return query(k<<1,l,r);
	else if (l>mid) return query(k<<1|1,l,r);
	else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read(),m=read();
	for (int i=1;i<=n;i++)
	{
		a[i]=read();
		pre[i]=p[a[i]];
		p[a[i]]=i;
	}
	memset(f,200,sizeof(f));f[0][0]=0;
	for (int j=1;j<=m;j++)
	{
		f[0][j]=0;
		build(1,0,n,j-1);
		for (int i=1;i<=n;i++)
		{
			add(1,pre[i],i-1);
			f[i][j]=query(1,0,i-1);
		}
	}
	cout<<f[n][m];
	return 0;
	//NOTICE LONG LONG!!!!!
}

  C:注意到事实上答案并不会很大,于是暴力枚举每个尾巴判断是否合法即可。至于如何判断,逐位考虑,如果该位上下界相同就填上并继续考虑下一位,如果有可以既不卡上界又不卡下界的数显然就合法了,否则考虑是填上界还是下界,填完之后就只会有一个边界很好处理了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 20
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;
}
ll l,r;
int n,m,s,a[N],b[N],f[N],g[N],ans;
bool calcup(int k)
{
	for (int i=k;i>=0;i--)
	{
		if (i+1<s) return 0;
		for (int j=1;j<f[i];j++)
		if (a[j]) return 1;
		if (f[i]&&i+1>s) return 1;
		if (f[i]==0||a[f[i]])
		{
			if (f[i]) a[f[i]]--,s--;
		}
		else return 0;
	}
	return s==0;
}
bool calcdown(int k)
{
	for (int i=k;i>=0;i--)
	{
		if (i+1<s) return 0;
		for (int j=g[i]+1;j<10;j++)
		if (a[j]) return 1;
		if (g[i]==0||a[g[i]])
		{
			if (g[i]) a[g[i]]--,s--;
		}
		else return 0;
	}
	return s==0;
}
void get()
{
	ans++;
	//for (int i=1;i<=9;i++) cout<<b[i]<<' ';cout<<endl;
}
void check()
{
	s=0;
	for (int i=1;i<=9;i++) s+=a[i]=b[i];
	for (int i=n-1;i>=0;i--)
	{
		if (i+1<s) return;
		for (int j=g[i]+1;j<f[i];j++)
		if (a[j]) {get();return;}
		if (g[i]==f[i])
		{
			if (f[i])
			{
				if (!a[f[i]]) return;
				a[f[i]]--;
				s--;
			}
		}
		else
		{
			if (a[f[i]])
			{
				int c[N];memcpy(c,a,sizeof(c));int tmps=s;
				a[f[i]]--;s--;
				if (calcup(i-1)) {get();return;}
				memcpy(a,c,sizeof(a));s=tmps;
			}
			if (a[g[i]]||g[i]==0)
			{
				if (g[i]) a[g[i]]--,s--;
				if (calcdown(i-1)) {get();return;}
			}
			return;
		}
	}
	if (s==0) get();
}
void dfs(int k,int s)
{
	if (k>9) {check();return;}
	for (int i=0;i<=18-s;i++)
	{
		b[k]=i;
		dfs(k+1,s+i);
	}
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	cin>>l>>r;
	while (r) f[n++]=r%10,r/=10;
	while (l) g[m++]=l%10,l/=10;
	dfs(1,0);
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  D:考虑点分。点分时考虑每条由根下去的链被统计了几次。化一下限制的式子容易发现是一个二维数点,bit套treap维护。常数小时限大二维数点O(nlog3n)就跑过去了。只是码力弱如我场上根本写不完。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<ctime>
using namespace std;
#define ll long long
#define N 100010 
#define P 1000000007
#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,p[N],len[N],size[N],t,ans=1,mx,mn;
bool flag[N];
struct data{int to,nxt,len,color;
}edge[N<<1];
struct data2{int x,y,x1,y1;
}deep[N];
namespace bitreap
{
	int root[N<<2],cnt=0;
	struct data{int ch[2],p,x,s;}tree[N<<5];
	void up(int k){tree[k].s=tree[lson].s+tree[rson].s+1;}
	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)
	{
	    if (k==0) {k=++cnt;tree[k].p=rand();lson=rson=0;tree[k].x=x;tree[k].s=1;return;}
	    tree[k].s++;
	    if (tree[k].x<x) {ins(rson,x);if (tree[rson].p>tree[k].p) move(k,1);}
	    else {ins(lson,x);if (tree[lson].p>tree[k].p) move(k,0);}
	}
	void del(int &k,int x)
	{
	    if (tree[k].x==x)
	    {
	        if (lson==0||rson==0) {k=lson|rson;return;}
	        if (tree[lson].p>tree[rson].p) move(k,0),del(rson,x);
	        else move(k,1),del(lson,x);
	    }
	    else if (tree[k].x<x) del(rson,x);
	    else del(lson,x);
	    up(k);
	}
	int query(int k,int x)
	{
	    if (k==0) return 0;
	    if (tree[k].x<x) return query(rson,x);
	    else return tree[rson].s+1+query(lson,x);
	}
	void Insert(int k,int x){k-=mn;k++;while (k<=mx-mn+1) ins(root[k],x),k+=k&-k;}
	void Delete(int k,int x){k-=mn;k++;while (k<=mx-mn+1) del(root[k],x),k+=k&-k;}
	int Query(int k,int x)
	{
		k-=mn;int s=0;
		while (k) s-=query(root[k],x),k^=k&-k;
		k=mx-mn+1;
		while (k) s+=query(root[k],x),k^=k&-k;
		return s;
	}
}
void addedge(int x,int y,int z,int c)
{
	t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,edge[t].color=c,p[x]=t;
}
int ksm(int a,int k)
{
	int s=1;
	for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P;
	return s;
}
void makes(int k,int from)
{
    size[k]=1;
    for (int i=p[k];i;i=edge[i].nxt) 
    if (edge[i].to!=from&&!flag[edge[i].to])
    {
        makes(edge[i].to,k);
        size[k]+=size[edge[i].to];
    }
}
int findroot(int k,int s,int from)
{
    int mx=0;
    for (int i=p[k];i;i=edge[i].nxt)
    if (edge[i].to!=from&&!flag[edge[i].to]&&size[edge[i].to]>size[mx]) mx=edge[i].to;
    if ((size[mx]<<1)>s) return findroot(mx,s,k);
    else return k;
}
void make(int k,int from)
{
	mx=max(mx,deep[k].x);mx=max(mx,deep[k].y);
	mn=min(mn,deep[k].x);mn=min(mn,deep[k].y);
	mx=max(mx,deep[k].x1);mx=max(mx,deep[k].y1);
	mn=min(mn,deep[k].x1);mn=min(mn,deep[k].y1);
	for (int i=p[k];i;i=edge[i].nxt)
	if (edge[i].to!=from&&!flag[edge[i].to])
	{
		deep[edge[i].to].x=deep[k].x;
		deep[edge[i].to].y=deep[k].y;
		if (edge[i].color>0) deep[edge[i].to].x++,deep[edge[i].to].y-=2;
		else deep[edge[i].to].y++,deep[edge[i].to].x-=2;
		deep[edge[i].to].x1=deep[k].x1;
		deep[edge[i].to].y1=deep[k].y1;
		if (edge[i].color>0) deep[edge[i].to].x1--,deep[edge[i].to].y1+=2;
		else deep[edge[i].to].y1--,deep[edge[i].to].x1+=2;
		len[edge[i].to]=1ll*len[k]*edge[i].len%P;
		make(edge[i].to,k);
	}
}
void work(int k,int from,int x)
{
	if (x==1) bitreap::Insert(deep[k].x1,deep[k].y1);
	else bitreap::Delete(deep[k].x1,deep[k].y1);
	for (int i=p[k];i;i=edge[i].nxt)
	if (edge[i].to!=from&&!flag[edge[i].to]) work(edge[i].to,k,x);
}
void calc(int k,int from)
{
	ans=1ll*ans*ksm(len[k],bitreap::Query(deep[k].x,deep[k].y))%P;
	for (int i=p[k];i;i=edge[i].nxt)
	if (edge[i].to!=from&&!flag[edge[i].to]) calc(edge[i].to,k);
}
void solve(int k)
{
	makes(k,k);
	k=findroot(k,size[k],k);
	flag[k]=1;deep[k].x=deep[k].y=deep[k].x1=deep[k].y1=0;len[k]=1;mx=mn=0;make(k,k);
	bitreap::cnt=0;
	for (int i=0;i<=mx-mn+1;i++) bitreap::root[i]=0;
	work(k,k,1);
	for (int i=p[k];i;i=edge[i].nxt)
	if (!flag[edge[i].to])
	{
		work(edge[i].to,edge[i].to,-1);
		calc(edge[i].to,edge[i].to);
		work(edge[i].to,edge[i].to,1);
	}
	for (int i=p[k];i;i=edge[i].nxt)
	if (!flag[edge[i].to]) solve(edge[i].to);
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
#endif
	srand(time(0));
	n=read();
	for (int i=1;i<n;i++)
	{
		int x=read(),y=read(),z=read(),c=read();if (c==0) c=-1;
		addedge(x,y,z,c),addedge(y,x,z,c);
	}
	solve(1);
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  E:注意到被超过两条线段覆盖的区间是一定无法作出贡献的。如果去掉这些区间,线段之间的关系看起来就比较友好了。

  看起来可能需要二分个答案,但就算单组能做也不太能拓展到多组询问上,于是不管。考虑扫描线,这样就能对每个前缀考虑删除线段的贡献了。

  每个时刻对线已扫过的部分,维护每个线段单独覆盖的区间长度、两线段只被这两条线段覆盖的区间交的长度(显然每条线段至多和其他两条线段有这样的交区间)。

  考虑当前区间被几条线段覆盖。

  如果没有,显然可以直接计入贡献。如果超过两条,显然不用管。

  如果是两条,就更新他们交的长度,并在此考虑且只考虑同时删除他们的答案。这样所有删除了两条有交线段的情况都可以被考虑到,因为每条线段与左端点在它之前的线段中的至多一条线段有交。

  如果只有一条,更新其单独覆盖的区间长度,并考虑选择它和其之前一条与其没有交的线段的最大贡献。这个东西显然随便拿一个数据结构都能维护了。

猜你喜欢

转载自www.cnblogs.com/Gloid/p/10514125.html