第 46 届 ICPC 国际大学生程序设计竞赛亚洲区域赛(上海)DEGHI

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;
}

你可能感兴趣的:(算法)