JSOI2015day1

2 篇文章 0 订阅
1 篇文章 0 订阅

题目好像没有放出来,自己去网上找吧

T1:

这题就是个裸贪心,直接记录每个点最多的收益以及是否唯一,然后更新的话,就把儿子收益排序,如果选到的儿子中有不唯一的,或者最后几个可以换的,或者有收益为0可选可不选的,就都不唯一

#include<cmath>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100011;
int tot=0,now[maxn],son[maxn<<1],pre[maxn<<1];
void add(int a,int b){pre[++tot]=now[a]; now[a]=tot; son[tot]=b;}
void cc(int a,int b){add(a,b); add(b,a);}
int n,w[maxn],lim[maxn];
void init(){
	scanf("%d",&n); w[1]=0; lim[1]=n+2; int a,b;
	for (int i=2;i<=n;++i) scanf("%d",w+i);
	for (int i=2;i<=n;++i) scanf("%d",lim+i),--lim[i];
	for (int i=2;i<=n;++i) scanf("%d%d",&a,&b),cc(a,b);
}
struct Tp{
	int f; bool ok;
	Tp(){}
	Tp(int f_,bool ok_):f(f_),ok(ok_){}
};
inline bool cmp_Tp_f(Tp a,Tp b){return a.f>b.f;}
int f[maxn]; bool ok[maxn];
void TreeDp(int x,int fa=0){
	for (int p=now[x];p;p=pre[p]) if (son[p]!=fa) TreeDp(son[p],x);
	static Tp a[maxn]; int sum=0;
	for (int p=now[x];p;p=pre[p]) if (son[p]!=fa) a[++sum]=Tp(f[son[p]],ok[son[p]]);
	sort(a+1,a+sum+1,cmp_Tp_f);
	f[x]=w[x]; int k=0; while (k<min(lim[x],sum) && a[k+1].f>0) f[x]+=a[++k].f;
	ok[x]=0; for (int i=1;i<=k;++i) ok[x]|=a[i].ok;
	if (k<sum && (!a[k+1].f || a[k+1].f==a[k].f)) ok[x]=1;
}
void work(){
	TreeDp(1); printf("%d\n",f[1]);
	puts(ok[1]?"solution is not unique":"solution is unique");
}
int main(){
	freopen("salesman.in","r",stdin); freopen("salesman.out","w",stdout);
	init(); work();
	fclose(stdin); fclose(stdout);
	return 0;
}

T2:

就是一道蘑菇题(就是那种难不死你也要烦死你的题),拿180对称举例,先把原数组a按180转一下得到b,然后判断一个矩阵x,y是否是180的话,就找到他转了后对应的坐标x1,y1,只要判断a[x][y]是否等于b[x1][y1]就行了(这里的x,y不是指一个点,而是整个矩阵的x,y ,也可以理解为每个点的x,y),于是要建7个矩阵,各种坐标转化,还要hash判断,这样是O(n^3)的,不要想过,毕竟7个,于是发现其实答案是有单调性的,但是奇偶要分开考虑,于是变成O(n^2logn),但是还是被卡常,改了好久最好把hash改成自然溢出才过了。。。(这题让我认识到了template的重要性)

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
#define CK(x) check_##x
#define CHK(x,len,i,j) check_##x(len,i,j)
using namespace std;
typedef unsigned int uint;
typedef unsigned long long ull;
const int maxn=511,d1=233,d2=2333;
uint h[maxn][maxn],h0[maxn][maxn],h1[maxn][maxn],
h2[maxn][maxn],h3[maxn][maxn],h4[maxn][maxn],h5[maxn][maxn];
uint Ppow[maxn],Qpow[maxn]; int n;
void init(){
	scanf("%d",&n); static char s[maxn];
	for (int i=1;i<=n;++i){
		scanf("%s",s+1);
		for (int j=1;j<=n;++j) h[i][j]=s[j]-'0';
	}
	Ppow[0]=1; for (int i=1;i<=n;++i) Ppow[i]=Ppow[i-1]*d1;
	Qpow[0]=1; for (int i=1;i<=n;++i) Qpow[i]=Qpow[i-1]*d2;
}
/*
0代表竖对称,1代表横对称,2代表主对角线对称,3代表副对角线对称 
4代表顺时针90度转,5代表180度转 
*/
void buildh(){
	for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) if (h[i][j])
		h0[i][n-j+1]=h1[n-i+1][j]=h2[j][i]=h3[n-j+1][n-i+1]=
		h4[j][n-i+1]=h5[n-i+1][n-j+1]=1;
}
void get(uint a[][maxn]){
	for (int i=1;i<=n;++i){
		int P=Ppow[n-i];
		for (int j=1;j<=n;++j){
			a[i][j]=a[i][j]*P*Qpow[n-j];
			a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
		}
	}
}
void gethash(){
	get(h),get(h0),get(h1),get(h2),get(h3),get(h4),get(h5);
}
inline uint gao(uint h[][maxn],int len,int x,int y){
	return (h[x][y]-h[x-len][y]-h[x][y-len]+h[x-len][y-len])
		*Ppow[x]*Qpow[y];
}
inline bool check_0(const int &len,const int &x,const int &y){
	uint tmp=gao(h,len,x,y);
	return tmp==gao(h0,len,x,n-y+len) || tmp==gao(h1,len,n-x+len,y)
	|| tmp==gao(h2,len,y,x) || tmp==gao(h3,len,n-y+len,n-x+len);
}
inline bool check_90(int &len,int &x,int &y){
	return gao(h,len,x,y)==gao(h4,len,y,n-x+len);
}
inline bool check_180(int &len,int &x,int &y){
	return gao(h,len,x,y)==gao(h5,len,n-x+len,n-y+len);
}
inline bool check_4(int &len,int &x,int &y){
	return CHK(180,len,x,y) && CHK(0,len,x,y);
}
inline bool check_8(int &len,int &x,int &y){
	return CHK(90,len,x,y) && CHK(0,len,x,y);
}
template <class T> bool check(int len,T f){
	for (int i=len;i<=n;++i)
		for (int j=len;j<=n;++j) if (f(len,i,j)) return true;
	return false;
}
template <class T> int calc(T f){
	int l,r,mid;
	for (l=1,r=n/2;mid=(l+r)/2,l<=r;)
		if (check(2*mid,f)) l=mid+1;else r=mid-1;
	--l; if (!check(2*l+1,f)) return 2*l;
	for (r=n/2;mid=(l+r)/2,l<=r;)
		if (check(2*mid+1,f)) l=mid+1;else r=mid-1;
	--l; return 2*l+1;
}
void solve(){
	printf("%d %d ",calc(CK(8)),calc(CK(90)));
	printf("%d %d ",calc(CK(4)),calc(CK(180)));
	printf("%d\n",calc(CK(0)));
}
void work(){
	buildh(); gethash(); solve();
}
int main(){
	freopen("symmetry.in","r",stdin); freopen("symmetry.out","w",stdout);
	init(); work();
	fclose(stdin); fclose(stdout);
	return 0;
}

T3:

这就是判断无根树同构,就是先找到重心(有两个的话就在两点间加一个新点作为重心),然后转化为有根树同构,记录每个点子树的hash值即可,树同构HK hash会被卡的毛都不剩(这题数据好像没有卡),由于记不住hash的正确姿势,就随便搞了一堆很复杂的东西,于是就Ok了。 顺便补一下正确的hash,先把儿子hash从小到大排序,然后每次H=(H*a^hash[son]+b)%c,H初值为1,a,b,c是常数,hash[x]=H; 这好像是有证明非常难卡的,以后要记住了。。。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxm=22,maxn=10011,Index=30077,mod=1000000009,Idx2=23333,md2=1000000007;
int m;
void init(){
	scanf("%d",&m);
}
int tot,now[maxn],pre[maxn<<1],son[maxn<<1],rd[maxn];
void add(int a,int b){pre[++tot]=now[a]; now[a]=tot; son[tot]=b; ++rd[b];}
struct Tp{
	int h1,h2;
	Tp(){}
	Tp(int h1_,int h2_):h1(h1_),h2(h2_){}
};
int hash1[maxn],hash2[maxn];
inline bool cmp_Tp_h1(Tp a,Tp b){return (a.h1>b.h1) || (a.h1==b.h1 && a.h2<b.h2);}
void gethash(int x,int fa){
	if (!x) return;
	if (rd[x]==2 && fa){
		for (int p=now[x];p;p=pre[p]) if (son[p]!=fa && son[p]) gethash(son[p],x);
		for (int p=now[x];p;p=pre[p]) if (son[p]!=fa && son[p])
			 hash1[x]=hash1[son[p]],hash2[x]=hash2[son[p]]; 
		return;
	}

	for (int p=now[x];p;p=pre[p]) if (son[p]!=fa && son[p]) gethash(son[p],x);
	static Tp a[maxn]; int sum=0;
	for (int p=now[x];p;p=pre[p]) if (son[p]!=fa && son[p])
		a[++sum]=Tp(hash1[son[p]],hash2[son[p]]);
	if (!sum){hash1[x]=hash2[x]=1; return;}
	sort(a+1,a+sum+1,cmp_Tp_h1); int r1=233,r2=2333;
	for (int i=1;i<=sum;++i) r1=(1LL*r1*Index+a[i].h1)%mod,
		r2=(1LL*r2*Idx2+1LL*a[i].h2*a[i].h2+1LL*rd[x]*a[i].h2+a[i].h1)%md2;
	hash1[x]=r1; hash2[x]=r2;
}
int n;
int find_root(){
	static int sz[maxn],q[maxn],fa[maxn]; int w=1; q[1]=1; fa[1]=0;
	memset(sz,0,sizeof(sz)); sz[1]=1;
	for (int i=1;i<=w;++i) for (int p=now[q[i]];p;p=pre[p])
		if (!sz[son[p]]) q[++w]=son[p],sz[q[w]]=1,fa[q[w]]=q[i];
	for (int i=1;i<=n;++i) if (rd[i]==2) sz[i]=0;
	for (int i=w;i>=1;--i) sz[fa[q[i]]]+=sz[q[i]];
	int a=-1; for (int i=n;i>=1;--i) if (sz[q[i]]*2>=sz[1]){a=q[i]; break;}
	if (sz[a]!=sz[1]/2) return a;
	int b=a; while (sz[fa[b]]==sz[1]/2) b=fa[b]; ++n;
	add(a,n); add(n,a); add(fa[b],n); add(n,fa[b]);
	for (int p=now[a];p;p=pre[p]) if (son[p]==fa[a]) son[p]=0;
	for (int p=now[fa[b]];p;p=pre[p]) if (son[p]==b) son[p]=0; return n;
}
void solve(int &h1,int &h2,int &s){
	scanf("%d",&n); int a,b; s=0;
	memset(rd,0,sizeof(rd)); tot=0; memset(now,0,sizeof(now));
	for (int i=1;i<n;++i) scanf("%d%d",&a,&b),add(a,b),add(b,a);
	for (int i=1;i<=n;++i) if (rd[i]!=2) ++s;
	int x=find_root(); gethash(x,0); h1=hash1[x]; h2=hash2[x];
}
void work(){
	static int hash1[maxm],hash2[maxm],s[maxm];
	for (int i=1;i<=m;++i) solve(hash1[i],hash2[i],s[i]);
	static bool ok[maxm];
	for (int i=1;i<=m;++i) for (int j=i+1;j<=m;++j){
		if (hash1[i]==hash1[j] && hash2[i]==hash2[j] && s[i]==s[j]) {ok[i]=1; break;}
	}
	static int a[maxm]; int sum=0;
	for (int i=1;i<=m;++i) if (!ok[i]) a[++sum]=s[i];
	sort(a+1,a+sum+1); cout<<sum<<endl;
	for (int i=1;i<=sum;++i) cout<<a[i]<<' '; puts("");
}
int main(){
	freopen("isomorphism.in","r",stdin); freopen("isomorphism.out","w",stdout);
	init(); work();
	fclose(stdin); fclose(stdout);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用[1],dp[u][j]表示在u子树中选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值