A
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
int n,k,s;
vector<int>v;
unordered_map<int,int>mp;
signed main(){
cin>>n>>k>>s;
for(int i=1;i<=k;i++){
int x;
cin>>x;
mp[x]=1;
}
for(int i=0;i<64;i++){
int t=s>>i&1;
if(t==1&&mp[i+1]==1){puts("NO");return 0;}
}
puts("YES");
}
B
dfs版本
#include<bits/stdc++.h>
using namespace std;
string s;
string a="cocacola";
int ans=0x3f3f3f3f;
void dfs(string x,int cnt){
if(x==a){
ans=min(ans,cnt);
return;
}
for(int i=0;i<8;i++){
if(x[i]==a[i])continue;
for(int j=0;j<8;j++){
if(x[j]!=a[j]&&x[j]==a[i]){
swap(x[i],x[j]);
dfs(x,cnt+1);
swap(x[i],x[j]);
}
}
}
}
signed main(){
cin>>s;
dfs(s,0);
cout<<ans;
}
bfs版本
从终点出发,用哈希表处理出到达每一步的min
感觉这个更好更加适用
#include<bits/stdc++.h>
using namespace std;
string s;
string a="cocacola";
int ans=0x3f3f3f3f;
unordered_map<string,int>mp;
queue<string>q;
void bfs(string start){
mp[start]=0;
q.push(start);
while(!q.empty()){
auto t=q.front();
q.pop();
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
if(t[i]==t[j])continue;
string x=t;
swap(x[i],x[j]);
if(mp.count(x))continue;
mp[x]=mp[t]+1;
q.push(x);
}
}
}
}
signed main(){
cin>>s;
bfs(a);
cout<<mp[s];
}
C
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=1e6+10;
int a[N][4];
int vis[N][4];
signed main(){
cin>>n>>m;
while(m--){
int x,y;
cin>>y>>x;
a[x][y]=1;
}
bool ok=1;
if(a[2][1]||a[1][2])ok=0;
for(int x=2,y=1;x<=n&&y<=2;){
vis[x][y]=1;
if(x<n&&a[x+1][y]==0)x++;
else if(a[x][y+1]==0)y++;
else {
ok=0;
break;
}
}
int x=1,y=2;
for(;x<n&&y<=3;){
if(a[x+1][y]==0&&vis[x+1][y]==0)x++;
else y++;
}
if(x!=x-1&&y!=3)ok=0;
if(ok)puts("YES");
else puts("NO");
}
D
prufer
我们可以轻松地发现这道题是一道dp的题目
对于整张图来说,总共有2*n-2地度数,由于每一个点都至少有一个度数,我们就只剩下n-2的度数可以进行自由分配
我们将每个点看做一个点加上一条没有连上其他点的边,然后在更新的时候将所有点联向当前树的叶子节点上,然后根据其度数做一下背包就好了
f[i] 表示树上已经有i 个点的最佳权值
假设度数为i的贡献为a[i],当一个节点插入的时候,会损失a[1]的价值,增加a[i-j+1]的价值,所以转移方程为
f[i]=max(f[i],f[j]+a[i-j+1]-a[1]),1<=j<=i;
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
#define int long long
int n;
int f[N];//f[i]表示分配i的度数的时候,整棵树权值和 最大能多大
int a[N];
signed main(){
cin>>n;
for(int i=1;i<n;i++)cin>>a[i];
memset(f,-0x3f,sizeof f);
f[0]=0;
for(int i=1;i<=n-1;i++){//枚举物品
for(int j=i;j<=n-2;j++){//枚举体积,(方案数
f[j]=max(f[j] , f[j-i]+a[i+1]-a[1]);
}
}
cout<<f[n-2]+n*a[1];
}
反思:
这道题难就难在怎么把这棵树的形状对应权值和的计算
== 转化为==
== 完全背包问题==
首先每个点都要加在树上
所以初始状态可以看成一个点连了一条边(没有连上另一个节点
总度数本来是2*(n-1)=n+n-2
减去初始的n个度数之后,题目就变成了如何将n-2的度数分配使得权值和最大
也就是说我现在有n-2块钱
要买2个鸡翅,4杯可乐,6个汉堡
这样分配我的12块钱可以使得价值最大化
所以说如果你理解了这个转化为完全背包的过程代码还可以这样写
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
#define int long long
int n;
int a[N];
int f[N];
signed main(){
cin>>n;
for(int i=1;i<n;i++)cin>>a[i];
memset(f,-0x3f,sizeof f);
f[0]=n*a[1];
for(int i=1;i<=n-2;i++){
for(int j=i;j<=n-2;j++){
f[j]=max(f[j] , f[j-i]+a[i+1]-a[1]);
}
}
cout<<f[n-2];
}
因为是完全背包,所以枚举体积的时候要正序枚举
E
数论+容斥原理
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
int T;
const ll inf=1e15,mod=998244353;
ll l,r,cnt[55][32],num[55][32];
ll solve(ll x){
memset(cnt,0,sizeof(cnt));
rep(i,0,50){
rep(j,0,25){
if(num[i][j]) cnt[i][j]=x/num[i][j];
}
}
ll ret=0;
per(k,0,75){
rep(i,0,min(k,50)){
int j=k-i;
if(j>25) continue;
if(cnt[i][j]){
ret+=((ll)(max(i,j)+1)*(cnt[i][j]-1));
rep(ii,0,i){
rep(jj,0,j){
cnt[ii][jj]-=cnt[i][j];
}
}
}
}
}
return ret;
}
int main(){
rep(i,0,50){
if(!i) num[i][0]=1;
else num[i][0]=2ll*num[i-1][0];
rep(j,1,25){
num[i][j]=num[i][j-1]*5ll;
if(num[i][j]>inf){
break;
}
}
}
cin>>T;
while(T--){
scanf("%lld%lld",&l,&r);
assert(l<=r&&l>0&&r>0&&r<=inf);
printf("%lld\n",(solve(r)-solve(l-1))%mod);
}
return 0;
}