华东交通大学2013年ACM“双基”程序设计竞赛 解题报告

华东交通大学2013年ACM“双基”程序设计竞赛 最终排名:http://acm.hdu.edu.cn/diy/contest_ranklist.php?cid=20955&page=1

华东交通大学2013年ACM“双基”程序设计竞赛赛后重挂(对题目感兴趣的同学可以去该网址重新提交代码进行测试):http://acm.hdu.edu.cn/diy/contest_show.php?cid=21280

下面给出所有题目的思路及源代码:

神犇的悲惨一生

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 432   Accepted Submission(s) : 243
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

传说中有位神犇,居住在公元250年前的压力山大,由于神犇一贯低调,所以人们连他活了多少岁都不知道,好在XXXX文献上有段关于他生平细节的文字。。
XX神犇的一生,幼年占了1/6,又过了1/12的青春期,又谈了1/6的恋爱后结婚,m年后生了个孩子,名叫神牛,神牛比神犇先死n年,神牛的寿命是神犇的一半
现在人们邀请作为下一任神犇的你,算出这位神犇活了多少岁。。(神犇的命都是好长的)

注意
请采用下面这种格式输入
#include<cstdio>
using namespace std;

int main()
{
int n , m;
while(scanf("%d%d",&n,&m) != EOF)
{

}
return 0;
}

Input

多组数据每组数据输入m,n,意思为题目所描述的。(0 < n , m <= 100)

Output

输一行出解(神犇的年龄)

Sample Input

50 70

Sample Output

1440

Author

moonlike


草稿纸上推算即可得出规律。
#include<cstdio>
using namespace std;

int main()
{
    int n , m;
    while(scanf("%d%d",&n,&m) != EOF)
    {
        printf("%d\n",12 * (n + m));
    }
    return 0;
}

Go shopping

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 622   Accepted Submission(s) : 35
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

最近Awell的运气特别好,这不,他在路边摊买彩票,居然中了大奖。秉着见者有份的原则,他准备请咱们学校ACM-ICPC训练基地的全体队员逛商场。

  赶巧交大旁边有一家商场新店开张,正在进行打折促销活动。于是,咱们所有队员都在商场中大肆购买之后,在收银台前排起了长队。

  话说回来,这家商场的打折方式有些奇怪:他们从在收银台前付账的所有n位顾客中,每隔m名顾客便挑选一位顾客享受七五折优惠,其余顾客只能享受九五折。

  为了方便付账,Awell拜托老板将付账者的姓名和付款金额打印出来,作为参考。
  
  你需要注意的是,在收银台前长长的队伍中,有的可不止是ACM队员,同样,还有很多交大的同学慕名前来消费。为了区分他们,我们规定,所有ACM队员必须在姓名前加上前缀“ACM”(不包含双引号,且不存在非ACM队员的同学名字前面出现ACM字样)。

  现在,请机智的你为Awell编写一个小程序,算一算他总共需要花费多少钱呢?

Input

输入数据包含多组,每组第一行有两个整数n,m。分别代表着在收银台前队伍的全部人数,以及商家将会选择每第m位顾客打7.5折。
你可以通过
while(scanf(......)!=EOF)
{
  ……;
}
的形式进行输入。接下来有n行,每行将会输入消费者的姓名(长度不超过20个字符),以及他们各自消费的金额(以“元”位单位,最高可能精确到小数点后两位)。

Output

  每组数据输出一行,每行一个实数,表示Awell总共需要花费多少开销。你应该注意的是,老板只收取“角”作为最小单位,而且他是一个锱铢必较的人,所以,如果你所付金额中存在小于0.1元的部分,那就至少要付0.1元给他(想着即将消瘦的钱包,Awell泪目中......O(∩_∩)O~~)

Sample Input

4 2
Newee 123.12
ACMAwell 100
PRO 345.5
Sirius 456.99
5 2
Newee 123.12
ACMAwell 100
PROPHET 345.5
Sirius 456.99
ACMProphetK 100

Sample Output

75.0
170.0

Author

ProphetK

按照题目要求模拟操作即可,注意因为输入的小数最多为2位,乘上0.75或0.95,累加之后最多为4位小数,所以只需要在运算结果上加上0.0499即可保证小于
0.1的可以进位。
#include<stdio.h>
int main()
{
    int n,m,i;
    double sum,buy;
    char name[25];
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        sum=0;
        for(i=1;i<=n;i++)
        {
            scanf("%s%lf",name,&buy);
            if(name[0]=='A'&&name[1]=='C'&&name[2]=='M')
            {
                if(i%m==0)
                    sum+=buy*0.75;
                else
                    sum+=buy*0.95;
            }
        }
        printf("%.1f\n",sum+0.0499);
    }
    return 0;
}


中秋挂灯笼

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 44   Accepted Submission(s) : 3
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

小明的家乡有一棵神奇的树-二叉树,即树的每一个节点只有两个分支

现在中秋节到了,乡里需要派一个人挂n个灯笼到这个树上(灯笼只能挂到树的节点上),由于风俗原因,当树上的一个节点挂了灯笼后,由这个节点开始产生的所有的分支都不准挂灯笼,已知每个灯笼都有一个重量W
而每个灯笼挂到树上所消耗的体力是树的根节点(地面)到灯笼的位置之间的距离L*W(两节点之间距离为1)
由于这个人体力有限,所以希望花费的体力最少,你能帮他算算他花费的最少体力是多少吗?

Input

多组测试
每行首先有个n(0<=n<=10000)表示灯笼的数量,接下来一行每行有n个数表示灯笼的重量W(0<=W<=10000)
输入以文件末尾结束

Output

对于每个测试输出一个整数表示花费的最少体力

第二个样例解释

Sample Input

4
1 1 2 1
1
1

Sample Output

15
1

Author

xyyh


本题主要考察数据结构——哈夫曼树。( 了解哈夫曼树请点击)

利用c++的优先队列很容易实现这一过程。
/*分析:
由于题目说:1.当树上的一个节点挂了灯笼后,由这个节点开始产生的所有的分支都不准挂灯笼
2.并且挂灯笼花费的力气是L*W,即到根的长度*权值
需要求挂完全部灯笼花费的最小力气,这就是完完全全的Huffman Tree模型
所以只要照着Huffman Tree建立树求最小值即可 
*/
//第二种写法:用优先队列
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;

const int MAX=10000+10;
int father[MAX*2],dp[MAX*2],s[MAX];

struct Node{
	int id,val;
	bool operator<(const Node &a)const{
		return val>a.val;
	}
}p,a,b;

void Init(int num){
    for(int i=0;i<2*num;++i){
        father[i]=i;
        dp[i]=0;
    }
}

int findset(int i){
    if(dp[i] || father[i] == i)return dp[i];
    return dp[i]=findset(father[i])+1;
}

int main(){
    int n;
    while(~scanf("%d",&n)){
    	priority_queue<Node>q;
        Init(n);
        for(int i=0;i<n;++i){
        	scanf("%d",&s[i]);
        	p.val=s[i],p.id=i;
        	q.push(p);
        }
        for(int i=0;i<n-1;++i){//做n-1次合并操作即可
            a=q.top(),q.pop();
            b=q.top(),q.pop();
            father[a.id]=father[b.id]=p.id=n+i;
            p.val=a.val+b.val;
            q.push(p);
        }
        __int64 sum=0;
        for(int i=0;i<n;++i){
            sum+=s[i]*(findset(i)+1);
        }
        printf("%I64d\n",sum);
    }
    return 0;
}

求和

Time Limit : 9000/3000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 47   Accepted Submission(s) : 0
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

定义一个关系:
f[n]=(1^x) * (x^1) + (2^x) * (x^2) + (3^x) * (x^3) + (4^x) * (x^4) + (5^x) * (x^5) +...+ (n^x) * (x^n)

定义另一个关系:
g[n]=k*n+b

现在给定x,k,n,b的值,求f[g[0]] + f[g[1]] + f[g[2]] + f[g[3]] + ...+ f[g[n-2]] + f[g[n-1]] + f[g[n]]
输出对20130919取模后的值

Input

输入有多组测试,每组测试输入4个正整数x,k,n,b代表题目描述的变量
其中0<x<=20,k,,n,b是不超过 1,000,000,000正整数
输入以文件末尾结束

Output

对于每组输入请输出 mod 20130919后的值

Sample Input

1 1 1 1
2 2 2 2

Sample Output

4
3814

Author

xyyh

/*分析:
假定f[n]=A^n;//A为矩阵,f[n]为A^n的某项值
则sum(f[g[n]])=f[g[0]]+f[g[1]]+...+f[g[n]]
=A^b+A^(k+b)+A^(2k+b)+A^(3k+b)+...+A^(nk+b)
=A^b+A^b(A^k+A^2k+A^3k+A^4k+...+A^nk)
将A^k看成一个新的矩阵B,则原式:
=A^b+A^b(B^1+B^2+B^3+...+B^n);//A^b,A^k用矩阵快速幂求出,括号中的用二分矩阵可求
所谓二分矩阵:A^1+A^2+A^3+A^4+A^5+A^6=(A^1+A^2+A^3)+A^3(A^1+A^2+A^3)

现在问题的关键转化为如何求矩阵A:
fn=1^x * x^1 + 2^x * x^2 +...+ n^x * x^n;
fn+1=1^x * x^1 + 2^x * x^2 +...+ n^x * x^n+(n+1)^x * x^(n+1)=fn+(n+1)^x * x^(n+1),将(n+1)^x二项式展开然后用矩阵快速幂
构造矩阵:
|1 xC(x,0) xC(x,1) xC(x,2) ... xC(x,x)|  |fn       | |f(n+1)           |
|0 xC(0,0) 0       0       ... 0      |  |x^n * n^0| |x^(n+1) * (n+1)^0|
|0 xC(1,0) xC(1,1) 0       ... 0      | *|x^n * n^1|=|x^(n+1) * (n+1)^1|
|0 xC(2,0) xC(2,1) xC(2,2) ... 0      |  |x^n * n^2| |x^(n+1) * (n+1)^2|
|...                                  |  |...      | |...              |
|0 xC(x,0) xC(x,1) xC(x,2) ... xC(x,x)|  |x^n * n^x| |x^(n+1) * (n+1)^x|
*/
//第一种方法,采用二分求A^1+A^2+A^3...+A^n
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
typedef __int64 LL;
using namespace std;

const int MAX=20+10;
const int mod=20130919;
LL array[MAX][MAX],ans[MAX][MAX];
LL temp[MAX][MAX],sum[MAX][MAX];
int n,x,k,b;

LL C(int n,int m){
	if(m<0 || m>n)return 0;
	LL num=1;
	for(int i=1;i<=m;++i){
		num=num*(n-m+i)/i;
	}
	return num%mod;
}

void MatrixInit(LL a[MAX][MAX],LL b[MAX][MAX],int flag){
	a[0][0]=1;
	if(flag == 2)a[0][0]=b[0][0];
	//计算第一行,总共有x+2行,x+2列
	for(int j=1;j<=x+1;++j){//C(x,0)~C(x,x)所以是x+1列
		if(flag == 1)a[0][j]=x*C(x,j-1)%mod;//初始化矩阵A
		else if(flag == 0)a[0][j]=0;//初始化单位矩阵
		else a[0][j]=b[0][j];//a=b
	}
	//计算第二行到第x+2行
	for(int i=1;i<=x+1;++i){
		for(int j=0;j<=x+1;++j){
			if(flag == 1)a[i][j]=x*C(i-1,j-1)%mod;//初始化矩阵A
			else if(flag == 0)a[i][j]=(i == j);//初始化单位矩阵
			else a[i][j]=b[i][j];//a=b
		}
	}
}

void MatrixAdd(LL a[MAX][MAX],LL b[MAX][MAX]){
	for(int i=0;i<=x+1;++i){
		for(int j=0;j<=x+1;++j){
			a[i][j]=(a[i][j]+b[i][j])%mod;
		}
	}
}

void MatrixMult(LL a[MAX][MAX],LL b[MAX][MAX]){
	LL c[MAX][MAX]={0};
	for(int i=0;i<=x+1;++i){
		for(int j=0;j<=x+1;++j){
			for(int k=0;k<=x+1;++k){
				c[i][j]+=a[i][k]*b[k][j];
			}
		}
	}
	for(int i=0;i<=x+1;++i){
		for(int j=0;j<=x+1;++j)a[i][j]=c[i][j]%mod;
	}
}

void MatrixPow(int k){
	MatrixInit(sum,sum,0);//sum=1
	MatrixInit(temp,array,2);//temp=array
	while(k){
		if(k&1)MatrixMult(sum,temp);
		MatrixMult(temp,temp);
		k>>=1;
	}
}

void MatrixSum(int k){//A^1+A^2+...+A^k=(A^1+A^2+...A^k/2)+A^m(A^1+A^2+...+A^k/2)=(1+A^m)*(A^1+A^2+...A^k/2)
	if(k == 1){MatrixInit(ans,array,2);return;}//ans=A,这里的A事上面分析的B
	MatrixSum(k/2);//A^1+A^2+...+A^k/2
	MatrixPow(k+1>>1);//A^m,m=(k+1)/2
	//k为偶数则(1+A^m)*(A+A^2+A^3...),m=(k+1)/2
	MatrixInit(temp,temp,0);//temp=1
	MatrixAdd(temp,sum);//temp=1+A^m
	MatrixMult(ans,temp);//ans=ans*temp
	//k为奇数则A^m+(1+A^m)*(A+A^2+A^3...),m=(k+1)/2
	if(k&1)MatrixAdd(ans,sum);//ans=A^m+ans,奇数的话A^1+A^2+...+A^k=(A^1+A^2+...A^k/2)+A^m+A^m(A^1+A^2+...+A^k/2)
}

int main(){
	while(~scanf("%d%d%d%d",&x,&k,&n,&b)){
		MatrixInit(array,array,1);//初始化array=A
		MatrixPow(k);//求sum=A^k
		MatrixInit(array,sum,2);//array=A^k=B
		MatrixSum(n);//求ans=B^1+B^2+...+B^n
		MatrixInit(array,array,1);//初始化array=A
		MatrixPow(b);//求sum=A^b
		MatrixMult(ans,sum);//ans=ans*sum=ans*A^b
		MatrixAdd(ans,sum);//ans=ans+A^b
		printf("%I64d\n",ans[0][1]);
	}
	return 0;
}

too_weak的奶酪

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 103   Accepted Submission(s) : 3
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

too_weak有一块N x N的棋盘,棋盘上K只老鼠,会吃掉和它处于同一行或同一列的奶酪,现在too_weak想要在棋盘上尽可能多地放置奶酪,要使得too_weak放的奶酪不会被老鼠吃掉。现在不能更弱的too_weak想要知道有多少个位置不能放奶酪(有老鼠的位置除外)。

Input

输入数据有很多组,以EOF结尾。
每组数据以两个整数N,K。N代表棋盘的规模是N x N,K代表老鼠的数目。
接下来有K行,每行两个数x,y。分别代表每只老鼠的坐标(下标从1开始)
1. 1 <= N<= 10^9,1<=k<=10^6
2. 1<=x,y<=10^9

Output

输出不能放置奶酪的位置有多少个。(除去有老鼠的位置)

Sample Input

4 4
1 2
2 1
2 3
3 2

Sample Output

11

Author

moonlike

可以先统计行被占用了多少,然后在统计列时去除对应重复的,再累加。
最后化简表达式可得到如下结果:

numx表示不同的行数量
numy表示不同的列数量
answer =  n*(numy+numx)-1ll*numy*numx-k

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 1000005;
int x[maxn],y[maxn];
int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        for(int i=0;i<k;++i)
           scanf("%d%d",&x[i],&y[i]);
        int numx=1,numy=1;
        sort(x,x+k);
        for(int i=1;i<k;++i)
            if(x[i]!=x[i-1]) ++numx;
       sort(y,y+k);
       for(int i=1;i<k;++i)
           if(y[i]!=y[i-1]) ++numy;
       printf("%I64d\n",1ll*n*(numy+numx)-1ll*numy*numx-k);
    }
    return 0;
}

Lentty要吃巧克力

Time Limit : 6000/2000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 57   Accepted Submission(s) : 5
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

Lentty最喜欢做的事情就是吃巧克力,经常幻想拥有吃不完的巧克力,作为一个acmer,kane出了个问题准备考考她,如果回答出来,那巧克力自然是源源不断的啦。kane给出了一列排好的的巧克力,有的是德芙,有的是费列罗,它们都拥有不同的美味值...现在kane通过魔法更改了这些巧克力,lentty必须能指出排列中第K个是巧克力的美味值是多少和某一段巧克力中最美味的值是多少,才能吃到巧克力,否则,哼哼,就去乖乖的做题吧。现在,lentty来寻求你的帮助,你能让poor lentty 吃上巧克力吗?

Input

输入数据有很多组,以EOF结尾。
每组数据以四个整数N,M。N代表初始的巧克力数目,M代表操作数。
第一行给定n,第二行含有n个正整数,代表每块巧克力的美味值wi。
每块巧克力的下标从0-n-1.。
操作分4种,Query x y 代表查询某一个区间内的美味最大值。
Ask x 代表查询某一块巧克力的美味值。
Change x y 代表将第x块的美味值变成y
Add x y 代表讲从第x块到第y块巧克力的美味值分别增加1.
1. 1 <= N<= 100000
2.1<= M <= 100000
2. Wi <= 5000

Output

对于每一个Query输出一个整数,代表区间内的美味最大值。
对于每一个Ask 输出一个整数,代表这块巧克力的美味值。

Sample Input

10 4
1 2 3 4 5 6 7 8 9 10
Ask 0
Change 0 1
Add 0 2
Query 0 2

Sample Output

1
4

Author

Mr.Ant

线段数的基本操作:单点更新,区间更新与最值查询。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define lz 2*u,l,mid
#define rz 2*u+1,mid+1,r
const int maxn=100005;
int a[maxn];
int maxx[4*maxn], flag[4*maxn];

void push_down(int u, int l, int r)
{
    if(flag[u])
    {
        flag[2*u]+=flag[u];
        flag[2*u+1]+=flag[u];
        maxx[2*u]+=flag[u];
        maxx[2*u+1]+=flag[u];
        flag[u]=0;
    }
}

void build(int u, int l, int r)
{
    flag[u]=0;
    if(l==r)
    {
        maxx[u]=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(lz);
    build(rz);
    maxx[u]=max(maxx[2*u],maxx[2*u+1]);
}

void Update(int u, int l, int r, int tl, int tr, int c, int op)
{
    if(tl<=l&&r<=tr)
    {
        if(op==1)
        {
            flag[u]+=c;
            maxx[u]+=c;
        }
        else
        {
            maxx[u]=c;
        }
        return ;
    }
    push_down(u,l,r);
    int mid=(l+r)>>1;
    if(tr<=mid) Update(lz,tl,tr,c,op);
    else if(tl>mid) Update(rz,tl,tr,c,op);
    else
    {
        Update(lz,tl,mid,c,op);
        Update(rz,mid+1,tr,c,op);
    }
    maxx[u]=max(maxx[2*u],maxx[2*u+1]);
}

int Query(int u, int l, int r, int tl, int tr)
{
    if(tl<=l&&r<=tr) return maxx[u];
    push_down(u,l,r);
    int mid=(l+r)>>1;
    if(tr<=mid) return Query(lz,tl,tr);
    else if(tl>mid) return Query(rz,tl,tr);
    else
    {
        int t1=Query(lz,tl,mid);
        int t2=Query(rz,mid+1,tr);
        return max(t1,t2);
    }
}

int main()
{
    int n, m;
    while(cin >> n >> m)
    {
        for(int i=1; i<=n; i++) scanf("%d",a+i);
        build(1,1,n);
        for(int i=1; i<=m; i++)
        {
            char ch[10];
            int x, y;
            scanf("%s",ch);
            if(strcmp(ch,"Add")==0)
            {
                scanf("%d%d",&x,&y);
                x++, y++;
                Update(1,1,n,x,y,1,1);
            }
            else if(strcmp(ch,"Change")==0)
            {
                scanf("%d%d",&x,&y);
                x++;
                Update(1,1,n,x,x,y,2);
            }
            else if(strcmp(ch,"Ask")==0)
            {
                scanf("%d",&x);
                x++;
                printf("%d\n",Query(1,1,n,x,x));
            }
            else
            {
                scanf("%d%d",&x,&y);
                x++,y++;
                printf("%d\n",Query(1,1,n,x,y));
            }
        }
    }
    return 0;
}

拯救之路

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 14   Accepted Submission(s) : 0
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

在忍者的世界里有众多的忍者村,鸣人和佐助出生在木叶村,他们从小一起学习忍术,但是佐助为了获得更强的力量离开了木叶村投奔了邪恶的大蛇丸。鸣人为了不让佐助被邪恶的力量所控制,决定只身前往大蛇丸的根据地拯救他。这里有n个忍者根据地,木叶村位于根据地1,大蛇丸位于根据地n,然后有m条双向的道路连接这n个根据地,每条路有一个特定的长度L。因为大蛇丸忍术高深,所以拯救之路注定不安逸。大蛇丸对每条路都施了一种忍术,每条路都用一个特定的字符标注(‘F’,‘U’,‘C’,‘K’中的一种),因此每条路鸣人只能按照特定的路线走方可到达大蛇丸的巢穴拯救佐助,鸣人走的路线必须是按照以下序列 ‘F’->’U’->’C’->’K’->’F’->’U’->’C’->’K’->.... etc,否则他是达到不了大蛇丸的巢穴的。为了让拯救之路更艰难,大蛇丸又施加了一种忍术,到达大蛇丸根据地时必须是走的完整的一个或者多个“FUCK”序列,这样才能拯救成功。
Note:为了让拯救行动更加容易,应该让拯救路线尽量短,同时应该让“FUCK”序列尽量长。

Input

  第一行输入一个整数T(1<=T<=500),表示测试数据的组数。
  每组测试数据有两个整数n(1<=n<=1500),m(1<=m<=20000),表示n个忍者根据地和m条道路。
  接下来输入m行,每行有4个变量 “u v L c”,表示这是一条路介于根据地u,v(1<=u,v<=n),这条路的长度是L(1<=L<=1000000),c是一个标记字符(‘F’,‘U’,‘C’,‘K’中的一种)。

Output

如果拯救行动无法完成,请输出Impossible。否则输出拯救路线的长度以及走过的“FUCK”序列个数。

Sample Input

2 
4 4 
1 2 1 F 
2 1 1 U 
1 3 1 C 
3 4 1 K 
4 4 
1 2 1 F 
2 3 1 U
3 4 1 C 
4 1 1 K

Sample Output

4 1
Impossible

Author

Mr.Ant

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long lld;
const int mn=3333;
const int mm=44444;
const lld oo=1e15;
int  reach[mm], next[mm], flow[mm], ch[mm];
lld  head[mn], que[mn], dis[mn][4], cnt[mn][4], inque[mn];
int n, edge;

void addedge(int u, int v, int c1, int c2,  char c)
{
    ch[edge]=c, reach[edge]=v, flow[edge]=c1, next[edge]=head[u], head[u]=edge++;
    ch[edge]=c, reach[edge]=u, flow[edge]=c2, next[edge]=head[v], head[v]=edge++;
}

int find(char c)
{
   if(c=='F') return 0;
   else if(c=='U') return 1;
   else if(c=='C') return 2;
   else return 3;
}

bool spfa()
{
    int l=0, h=0;
    memset(inque,0,sizeof(inque));
    for(int i=1; i<=n; i++)
        for(int j=0; j<4; j++) dis[i][j]=oo, cnt[i][j]=0;
    inque[1]=1;
    dis[1][0]=0;
    que[l++]=1;
    while(l!=h)
    {
        int u=que[h++];
        if(h==mn) h=0;
        inque[u]=0;
        for(int i=head[u]; i>=0; i=next[i])
        {
            int s=find(ch[i]), v=reach[i], val=flow[i];
            if(dis[v][(s+1)%4]>=dis[u][s]+val)
            {
                if(dis[v][(s+1)%4]==dis[u][s]+val)
                {
                    if(cnt[u][s]+1>cnt[v][(s+1)%4]) cnt[v][(s+1)%4]=cnt[u][s]+1;
                    else continue;
                }
                else
                {
                    dis[v][(s+1)%4]=dis[u][s]+val;
                    cnt[v][(s+1)%4]=cnt[u][s]+1;
                }
                if(!inque[v])
                {
                    inque[v]=1;
                    que[l++]=v;
                    if(l==mn) l=0;
                }
            }
        }
    }
    if(dis[n][0]==oo||!cnt[n][0]) return false;
    else return true;
}

int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int m, T, tcase=0;
    cin >> T;
    while(T--)
    {
        cin >> n >> m;
        edge=0;
        memset(head,-1,sizeof(head));
        int mp[4]={0,0,0,0}, ct=0;
        while(m--)
        {
            int u, v, val, se;
            char sh[3];
            scanf("%d%d%d%s",&u,&v,&val,sh);
            addedge(u,v,val,val,sh[0]);
            if(n==1&&u==1&&v==1)
            {
                se=find(sh[0]);
                if(!mp[se]) ct++, mp[se]=val;
                else mp[se]=min(mp[se],val);
            }
        }
        if(ct==4)
        {
           lld sum=mp[0]+mp[1]+mp[2]+mp[3];
           printf("%I64d %d\n",sum,ct/4);
           continue;
        }
        bool ok=spfa();
        if(!ok) puts("Impossible");
        else printf("%I64d %I64d\n",dis[n][0],cnt[n][0]/4);
    }
    return 0;
}

ZEROm的乘法运算

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 24   Accepted Submission(s) : 1
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

ZEROm要too_weak快速计算下列程序的运算结果Sum,too_weak必然是不会的,现在请你帮助他.
int Sum=0;
for(int i=1;i<=N;i++)
  for(int j=i+1;j<=N;j++)
   for(int k=j+1;k<=N;k++)
   Sum+=a[i]*a[j]*a[k];

Input

输入数据有很多组,以EOF结尾。(大规模输入。请用scanf)
每组数据的第一行包含一个正整数N(1 <= N<= 1000000),说明第二行有N个数。
第二行有N个数,分别代表a[1],a[2],a[3]....a[N].(0<=a[i]<=10^9)

Output

因为Sum可能很大,输出Sum对9973求余的结果。

Sample Input

5
1 2 3 4 5

Sample Output

225

Author

moonlike

容斥原理的简单应用。

#include <cstdio>
using namespace std;
typedef long long LL;
const int mod = 9973;
const int maxn =1000000+5;
LL a[maxn];
int main()
{
    int n;
    while(~scanf("%d",&n)){
        LL sum=0,xxx=0;
        for(int i=0;i<n;i++){
            scanf("%I64d",&a[i]);
            sum=(sum+a[i])%mod;
            xxx=xxx+a[i]*a[i]%mod*a[i]%mod;
        }
        for(int i=0;i<n;i++)
            xxx=(xxx+3*a[i]*a[i]%mod*(sum-a[i])%mod)%mod;
        sum=sum*sum%mod*sum%mod;
        sum=((sum-xxx)%mod+mod)%mod;
        printf("%I64d\n",sum*8311%mod);
    }
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值