8.18NOIP Day4模拟赛
T1
观察题目后可以发现当两个用户不在同一场比赛出现过可能为同一个人的号,考虑状压,然后按位与等于零即可合并
由于m极小简单证明一下即可发现能合并就合并一定不劣,所以开个计数数组就秒了,记得把没出现的放到最后面
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int x[N],cnt[50],ans=0;
bool cmp(int a,int b){return a>b;}
signed main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int u,v;
scanf("%d",&u);
for(int j=1;j<=u;j++){
scanf("%d",&v);
x[v]+=(1<<i);
}
}
int ans=0;
sort(x+1,x+n+1,cmp);
for(int i=1;i<=n;i++){
int flag=0;
for(int j=(1<<m)-1;j>=0;j--)if(!(j&x[i])&&cnt[j]){
cnt[j]--;
cnt[j|x[i]]++;
flag=1;
break;
}
if(!flag){
ans++;
cnt[x[i]]++;
}
}
printf("%d\n",ans);
return 0;
}
T2
考虑枚举唯一一个度数大于 k 的点,剩下的都是度数小于等于 k 的,换根 DP 即可
#include<bits/stdc++.h>
#define N 200005
#define int long long
using namespace std;
int n,k,dp[N],dis[N],to[N],num[N],mint[N],minn[N],ans=0,top=0;
struct Ty {int v,val;};
vector<Ty>y[N];
bool cmp(int a,int b){return a>b;}
void dfs(int u,int fa){
for(int i=0;i<y[u].size();i++)if(y[u][i].v!=fa){
dfs(y[u][i].v,u);
dis[u]+=dp[y[u][i].v]+y[u][i].val;
}
top=0;
for(int i=0;i<y[u].size();i++)if(y[u][i].v!=fa)num[++top]=dp[y[u][i].v]+y[u][i].val;
sort(num+1,num+top+1,cmp);
for(int i=1;i<=min(top,k-1);i++)dp[u]+=num[i];
if(top<k)num[min(top,k-1)+1]=0;
minn[u]=num[min(top,k-1)];
mint[u]=num[min(top,k-1)+1];
return;
}
void solve(int u,int fa){
ans=max(ans,dis[u]+to[fa]);
for(int i=0;i<y[u].size();i++)if(y[u][i].v!=fa){
to[u]=dp[u]+y[u][i].val;
if(k>1){
if(dp[y[u][i].v]+y[u][i].val>=minn[u]){
to[u]-=dp[y[u][i].v]+y[u][i].val;
to[u]+=max(mint[u],to[fa]);
}
else {
to[u]-=minn[u];
to[u]+=max(minn[u],to[fa]);
}
}
solve(y[u][i].v,u);
}
return;
}
signed main(){
scanf("%lld%lld",&n,&k);
if(k==0){
printf("0\n");
return 0;
}
for(int i=1;i<n;i++){
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
y[u].push_back({v,w});
y[v].push_back({u,w});
}
dfs(1,0);
solve(1,0);
printf("%lld\n",ans);
return 0;
}
T3
先鸽了
考虑转化题意,那么二元组 \((a,b)\) 合法当且仅当存在若干个矩形使得面积和为 \(a\),周长和为 \(b\)。
我们可以将宽相同的矩形合并,那么合并到最后一定只剩下一车 \(1\times 1\) 和若干个宽互不相同的矩形。
注意到 \(1\times 1\) 不影响 \(2a-b/2\) 的值,那么就处理出对于每一个 \(2a-b/2\) 的 \(b\) 最小是多少合法,跑个背包即可,最后统计答案
#include<bits/stdc++.h>
#define int long long
#define N 200005
using namespace std;
int dp[N*2],f[N*2];
signed main(){
int s,c;
scanf("%lld%lld",&s,&c);
c=c/2;
for(int i=0;i<=s*2;i++)dp[i]=c+1;
for(int i=1;i<=c-1;i++)dp[i-1]=i+1;
for(int i=2;i*i<=s;i++){
for(int j=0;j<=s*2;j++)f[j]=dp[j];
f[0]=0;
for(int j=0;j<=s*2;j++){
if(j>=i*2-1)f[j]=min(f[j],f[j-(i*2-1)]+1);
if(j>=2*i*(i-1))dp[j]=min(dp[j],f[j-2*i*(i-1)]+i*2);
}
}
int ans=0;
for(int i=0;i<=s*2;i++)if(dp[i]<=c){
int cnt=min(s-(dp[i]+i)/2+1,(c-dp[i])/2+1);
if(cnt>=0)ans+=cnt;
}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号