题目
D
题意: 给定p/q,求是否存在两个正整数a、b,满足a/b+b/a=p/q.
思路: 假设x=a/b,然后发现就是个一元二次方程,然后如果Δ不是某个数的平方的话,是不符合题意的,因为x不能是无理数的。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k,T;
//set<ll> sa;
void solve()
{
ll p;
int q; scanf("%lld%d",&p,&q);
ll t = 1ll*p*p-4ll*q*q;
// ll tmp = sqrt(t);
if(t>=0)
{
ll tmp = sqrt(t);
if(tmp*tmp!=t) {printf("0 0\n"); return ;}
int x = p+tmp;
int y = 2*q;
printf("%d %d\n",x,y);
}
else printf("0 0\n");
}
int main(void)
{
scanf("%d",&T);
while(T--)
solve();
return 0;
}
G
题意: 给定一棵n个节点的树,保证n是奇数,然后我们需要把这n-1条边分成互不相交的n-1/2组,即每两条边在一个组里,然后要求是这两条边必须有一个公共顶点
思路: 猜结论。先随便找个点当根,dfs预处理每个节点的深度。之后统计答案。叶子不算,从叶子往上的一层开始。如果有偶数个儿子,自己分配,Cn2、Cn-22这样的,然后如果是奇数个儿子,留着一个儿子和该节点连向父节点的边配合,剩下的选俩、选俩直到都选完。
PS: 记得除(tot)!,tot是每个节点分了多少组,因为这样算有会有重复。没考虑到,输。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const int mod = 998244353;
int n,m,k,T;
vector<int> va[N],v[N];
int level[N];
int mx;
int d[N];
int f[N];
ll inv[N];
ll fac[N];
void dfs(int cur,int fa)
{
// cout<<cur<<":"<<l<<"??\n";
f[cur] = fa; level[cur] = level[fa] + 1;
for(int i=0;i<va[cur].size();++i)
{
int j = va[cur][i];
if(j==fa) continue;
dfs(j,cur);
}
v[level[cur]].push_back(cur);
mx = max(mx,level[cur]);
}
ll qpow(ll a,int k)
{
ll res = 1;
while(k)
{
if(k&1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
ll ni(int x)
{
return qpow(x,mod-2);
}
void solve()
{
ll ans = 1;
scanf("%d",&n);
for(int i=0;i<n-1;++i)
{
int x,y; scanf("%d%d",&x,&y);
va[x].push_back(y),va[y].push_back(x);
d[x]++,d[y]++;
}
int root = 1;
dfs(root,0);
for(int t=mx;t>=1;--t)
{
for(int i=0;i<v[t].size();++i)
{
int u = v[t][i];
int tot = 0;
// if(d[u]&1) tot = (d[u]-1)/2;
// else tot = (d[u]-2)/2;
// cout<<u<<":"<<d[u]<<" "<<tot<<":"<<inv[tot]<<"\n";
d[u]--;
while(d[u]>=2)
{
ans = ans * 1ll * d[u] % mod * (d[u]-1) % mod * ni(2) % mod;
d[u] -= 2;
tot ++ ;
}
ans = ans * inv[tot] % mod;
if(d[u]==1) d[u]--,d[f[u]]--;
}
}
ans %= mod; ans = (ans + mod) % mod;
printf("%lld",ans);
}
int main(void)
{
fac[0] = inv[0] = 1;
for(int i=1;i<N;++i)
{
fac[i] = fac[i-1] * i % mod;
}
inv[N-1] = ni(fac[N-1]);
for(int i=N-2;i>=1;--i)
{
inv[i] = inv[i+1] * (i+1) % mod;
}
solve();
return 0;
}
I
题意: 给定n个物品,有体积和价值。可以选m个体积翻倍。任选一些物品分成两个不相交的集合,满足两个集合的体积和相等的前提下,使得价值和最大。
思路: 01背包变种。
f[i][j][k]: 前i个物品,有j个翻倍,然后集合1的体积和-集合2的体积和为k。
注意可能是负数,所以要整个偏移量。
状态转移的话,每个物品有选、集合1以及翻倍不翻倍、集合2以及翻倍不翻倍。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 102;
int n,m,k,T;
ll f[N][N][N*26*2];
int v[N];
int w[N];
int sum = 0;
void solve()
{
ll ans = 0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d%d",&w[i],&v[i]),sum += v[i];
// for(int i=1;i<=n;++i) scanf("%d",&v[i]),sum += v[i];
sum *= 2;
memset(f,-0x3f,sizeof(f));
f[0][0][sum] = 0;
for(int i=1;i<=n;++i)
{
for(int j=0;j<=min(i,m);++j)
{
for(int k=-sum+sum;k<=sum+sum;++k)
{
f[i][j][k] = max(f[i][j][k],f[i-1][j][k]); //不选
if(k-v[i]>=0&&k-v[i]<=2*sum)
f[i][j][k] = max(f[i][j][k],f[i-1][j][k-v[i]]+w[i]); //选左
if(j>=1&&k-2*v[i]>=0&&k-2*v[i]<=2*sum)
f[i][j][k] = max(f[i][j][k],f[i-1][j-1][k-2*v[i]]+w[i]); //选左且翻倍
if(k+v[i]>=0&&k+v[i]<=2*sum)
f[i][j][k] = max(f[i][j][k],f[i-1][j][k+v[i]]+w[i]); //选右
if(j>=1&&k+2*v[i]>=0&&k+2*v[i]<=2*sum)
f[i][j][k] = max(f[i][j][k],f[i-1][j-1][k+2*v[i]]+w[i]); //选右且翻倍
}
}
}
// cout<<f[1][0][sum-1]<<"???\n";
for(int j=0;j<=m;++j)
{
// cout<<j<<":"<<f[n][j][sum]<<"?\n";
ans = max(ans,f[n][j][sum]);
}
printf("%lld",ans);
}
signed main(void)
{
solve();
return 0;
}