D
Strange_Fractions
令t=b/a,可以得到关于t的一元二次方程,用根的存在定理以及根是否为整数判断是否存在解,若有解,用求根方式求出t来,取t=(p+sqrt(p^2 -4q^2))/(2*q)(为了保证结果为正数),然后让a,b分别等于分子,分母
code:
#include
#define DEBUG freopen("_in_.txt","r",stdin);
typedef long long ll;
using namespace std;
ll t, p, q;
int main()
{
// DEBUG;
scanf("%lld", &t);
while (t--)
{
scanf("%lld%lld", &p, &q);
ll temp=p*p-4*q*q;
if(temp<0)
{
printf("0 0\n");
continue;
}
ll c=(ll)sqrt(temp);
if(c*c!=temp)
{
printf("0 0\n");
continue;
}
ll a=2*q;
ll b=p+c;
ll fen=__gcd(a,b);
printf("%lld %lld\n",a/fen,b/fen);
}
}
E
Strange_Integers
题目等价于在排序好的数组里找最多有多少相邻大于k的数,转化后就十分好写了
Code:
#include
#include
using namespace std;
int n,k,a[100010],ans;
bool cmp(int a,int b)
{
return a<b;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1,cmp);
int temp=a[1];ans=1;
for(int i=2;i<=n;i++)
{
if(a[i]-temp>=k)
{
temp=a[i];
ans++;
}
}
printf("%d",ans);
}
G
Edge Groups
思想是先将整个图划分成许多个块,每个块有一个中心点,所有边都连着这个中心点。划分方法就是按照拓扑排序的方法,从叶子节点开始,找到其父节点,但连接两者的边是归属于子节点还是父节点呢,就要看已经归属于子节点的边是奇数个还是偶数个,若为奇数个,归属到字节点,否则归属到父节点。划分好后,每个块内有多少方案就是n个数两两一组的方案,块与块之间相乘即可
Code:
#include
#include
#include
#define p 998244353
using namespace std;
long long n,b[100010],guishu[100010],du[100010],head[100010],k,sum,ans=1,J[100010];
queue<long long> q;
struct edge
{
long long to,next;
} ed[400010];
long long kissme(long long x,long long k)
{
long long sum=1,base=x;
while(k)
{
if(k&1)
{
sum*=base;
sum%=p;
}
base*=base;
base%=p;
k>>=1;
}
return sum;
}
long long Ni(long long x)
{
return kissme(x,p-2);
}
void adde(long long u,long long v)
{
ed[++k].to=v;
ed[k].next=head[u];
head[u]=k;
}
int main()
{
// freopen("text.txt","r",stdin);
scanf("%lld",&n);
J[0]=1;
for(long long i=1;i<=n;i++)
J[i]=J[i-1]*i%p;
for(long long i=1;i<=n-1;i++)
{
long long u,v;
scanf("%lld%lld",&u,&v);
adde(u,v);
adde(v,u);
du[u]++;
du[v]++;
}
for(long long i=1;i<=n;i++)
if(du[i]==1) q.push(i);//由于是无向图,叶子节点度数为1
while(!q.empty())
{
long long u=q.front();q.pop();
if(guishu[u]&1)
{
for(long long i=head[u];i;i=ed[i].next)
{
long long v=ed[i].to;
if(b[v]) continue;
guishu[u]++;
du[v]--;
if(du[v]==1) q.push(v);
}
b[u]=1;du[u]--;
}
else
{
for(long long i=head[u];i;i=ed[i].next)
{
long long v=ed[i].to;
if(b[v]) continue;
guishu[v]++;
du[v]--;
if(du[v]==1) q.push(v);
}
b[u]=1;du[u]--;
}
}
for(long long i=1;i<=n;i++)
{
if(!guishu[i]) continue;
if(guishu[i]&1) printf("*");
ans*=(J[guishu[i]]*(Ni(kissme(2,guishu[i]>>1))%p)%p*(Ni(J[guishu[i]>>1])%p))%p;
ans=ans%p;
}
// printf("%lld\n",kissme(2,5));
printf("%lld",ans);
return 0;
}
H
Life is a Game
这场铜牌题,也是我们队最可惜的题,赛中有思路的时候已经很晚了,debug时间不够,赛后十几分钟过了,含泪打铁。。
可以用并查集做,首先将所有边去掉,整张图变成n个孤立的点,将所有边从小到大排序,用优先队列维护询问,当询问的点所在的块能量大于边权时,连上这条边,把边两端点合并到一个块,并更新块能量,直到所有边都连完
Code:
#include
// #define DEBUG freopen("_in.txt", "r", stdin);
#define DEBUG freopen("_in.txt", "r", stdin), freopen("_out.txt", "w", stdout);
typedef long long ll;
using namespace std;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll maxn = 3e5 + 10;
const ll maxm = 2e4 + 10;
const ll mod = 998244353;
ll n,m,q;
ll arr[maxn],f[maxn],zhi[maxn];
struct Edge
{
ll u,v,w;
bool operator<(Edge other)const
{
return w<other.w;
}
}edges[maxn];
struct Node
{
ll val,st,tmp,id;
bool operator<(Node other)const
{
return val>other.val;
}
}nodes[maxn];
priority_queue<Node> que;
ll fd(ll x)
{
if(f[x]==x) return x;
else return f[x]=fd(f[x]);
}
void hb(ll x,ll y)
{
ll fx=fd(x),fy=fd(y);
if(fx==fy) return;
zhi[fx]+=zhi[fy];
// zhi[fy]=0;
f[fy]=f[fx];
}
bool cmp(Node a,Node b)
{
return a.id<b.id;
}
ll ans[maxn];
int main()
{
scanf("%lld%lld%lld",&n,&m,&q);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&arr[i]);
f[i]=i;
zhi[i]=arr[i];
}
for(ll i=1;i<=m;i++)
{
ll u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
edges[i]={u,v,w};
}
sort(edges+1,edges+m+1);
edges[m+1].w=INF;
for(ll i=1;i<=q;i++)
{
scanf("%lld%lld",&nodes[i].st,&nodes[i].val);
nodes[i].id=i;
que.push(nodes[i]);
}
ll cnt=1;
while(!que.empty())
{
Node temp=que.top();
que.pop();
while(temp.val>=edges[cnt].w)
{
hb(edges[cnt].u,edges[cnt].v);
cnt++;
}
temp.val-=temp.tmp;
temp.tmp=zhi[fd(temp.st)];
temp.val+=temp.tmp;
ans[temp.id]=temp.val;
if(temp.val>=edges[cnt].w)
{
que.push(temp);
}
}
for(ll i=1;i<=q;i++)
{
printf("%lld\n",ans[i]);
}
}
I
Steadily Growing Steam
背包,dp[i][j][k]表示当到第i张牌时,A,B差为j,用了k次翻倍魔法,用滚动数组优化
Code:
#include
// #define DEBUG freopen("_in_.txt", "r", stdin);
#define DEBUG freopen("_in_.txt", "r", stdin),freopen("_out_.txt", "w", stdout);
typedef long long ll;
using namespace std;
const ll maxn = 1e2 + 10;
const ll maxm = 2e4 + 10;
ll n, m,F;
ll val[maxn], poi[maxn];
ll dp[2][maxm][maxn];
ll ans=0;
int main()
{
// DEBUG;
scanf("%lld%lld", &n, &m);
for (ll i = 1; i <= n; i++)
{
scanf("%lld%lld", &val[i], &poi[i]);
}
memset(dp, -63, sizeof(dp));
dp[0][(ll)1e4][0] = 0;
dp[1][(ll)1e4][0] = 0;
for (ll i = 1; i <= n; i++)
{
F^=1;
for (ll j = 1e4 - 3000; j <= 1e4 + 3000; j++)
{
for (ll k = m; k>=0 ; k--)
{
dp[F][j][k]=max(dp[F][j][k],dp[F^1][j][k]);
dp[F][j][k]=max(dp[F][j][k],dp[F^1][j+poi[i]][k]+val[i]);
dp[F][j][k]=max(dp[F][j][k],dp[F^1][j-poi[i]][k]+val[i]);
if(!k) continue;
dp[F][j][k]=max(dp[F][j][k],dp[F^1][j+poi[i]*2][k-1]+val[i]);
dp[F][j][k]=max(dp[F][j][k],dp[F^1][j-poi[i]*2][k-1]+val[i]);
}
}
}
for(int i=0;i<=m;i++)
{
ans=max(ans,dp[F][(ll)1e4][i]);
}
printf("%lld",ans);
return 0;
}