大意:
输入 n(2≤n≤1e5) k(2≤k≤100) 和一棵无向树的 n-1 条边(节点编号从 1 开始),每条边包含 3 个数 x y c,表示有一条颜色为 c 的边连接 x 和 y,其中 c 等于 0 或 1。
对于长为 k 节点序列 a,走最短路,按顺序经过节点 a1 -> a2 -> ... -> ak。
对于所有长为 k 的节点序列 a(这有 n^k 个),统计至少经过一条 c=1 的边的序列 a 的个数。
思路:由图可得,总共有n^k个序列,要求至少经过一条 c=1 的边的序列 a 的个数,由容斥定理可得,我们需要求出没有经过c=1的边的序列b的个数。然后n^k 减去该数即可。
由题可得,若两个点之间存在一条c=1的边,那么必然这两个点必然不能计入b的个数中,故可视作c=1的边不会使得两个点连通。故我们可确定若干个由c!=1 连接的连通块。由于序列可重复,故序列b的个数是每个连通块中的节点个数t的 t^k 的和。 计算出b的个数后再用所有结果减去b即可。
代码:
#include <iostream>
#include <cstring>
#include <queue>
#define x first
#define y second
using namespace std;
typedef long long LL;
const int N =1e5+10 , mod = 1e9 +7 ;
int pre[ N] ;
int v[N] ;
int find( int x ){
if( x == pre[x]) return x;
return pre[x] = find( pre[x] ) ;
}
void merge( int a ,int b ){
int pa = find( a ) , pb = find( b ) ;
v[pa] += v[pb];
pre[pb] = pa;
}
LL qmi( LL a , LL b){
LL res =1;
while( b ){
if( b&1) res = ( res *a) % mod;
b >>=1;
a = (a*a) %mod;
}
return res ;
}
int main()
{
int T ; T=1;
while( T --){
int n , k;
cin >> n >> k ;
for(int i = 1 ; i <= n ; ++i ) pre[i] = i ,v[i] = 1 ;
for(int i = 0 ; i < n-1 ; ++i){
int a,b,c;
cin >>a >> b>>c;
if( c) continue;
int pa= find(a ) , pb = find( b ) ;
if( pa== pb) continue;
merge( a, b) ;
}
LL res = qmi( n , k ) ;
for(int i = 1 ; i <=n; ++i){
if( pre[i] == i){
//cout<<res <<' ' <<qmi( v[i] , k ) <<endl;
res = ( res - qmi( v[i] , k) +mod)%mod ;
}
}
cout<< res <<endl;
}
return 0 ;
}
问题二:1790E - Vlad and a Pair of Numbers
大意:已知 x= a^b = (a+b)/2 ,现给出t个x,若通过x能确定符合条件的a,b则输出,反之输出-1。
思路: 首先我们需要了解一个异或求和公式:
a +b = (a ^ b ) + ( (a &b ) << 1 )
而由题可得,x = a ^ b = (a &b ) << 1 ,故我们可以设 y = a & b = x>>1
而因为任何数左移一位后相当于扩大两倍为偶数,故x为奇数时不存在符合条件的a,b。
x为偶数时, 我们可以设a,b初值为0 ,当第i位上,
x& y = 1 , x^ y = 0 时,显然这个位置上a和b的分配都是1,1 ;
当x&y =1, x^y = 1时,显然不存在符合题意的a,b;
当x&y =0, x^y = 0 时, 显然这个位置上a和b的分配都是0,0;
当x&y =0 , x^y = 1时,显然分别是0,1。
每次分配1时,将1给非0的a,b其中之一。若最终结果存在a或b为0,则不符合条件。
反之,则符合条件。
代码:
#include <iostream>
#include <cstring>
#include <queue>
#define x first
#define y second
using namespace std;
typedef long long LL;
const int N =1e5+10 , mod = 1e9 +7 ;
int x ;
int main()
{
int T ; cin >>T;
while( T --){
cin >> x ;
if( x & 1) {
cout<<"-1" <<endl;continue;
}
int y = x>>1;
bool flag = true;
int a= 0 , b = 0 ;
for(int i = 0 ; i <=29; ++i ){
int dx = x & ( 1<<i) , dy = y & ( 1<<i ) ;
if( dx && dy) {
flag= false; break;
}
if( !dx && dy){
a |= 1<< i ; b |= 1<< i ;
}else if( dx && !dy) {
if( !a ) a |= 1 <<i;
else b |= 1<< i;
}
}
if( !a || !b || !flag ) cout<<-1<<endl;
else cout<< b <<' ' <<a << endl;
}
return 0 ;
}
大意:给定n个长度的数组a, 每次能取出连续的数字。问最少要取多少次。
比如:[2,2,3,4,3,1],取一次,[1,2,3,4],取第二次, [2,3]
思路:存取每个数字存在的次数,每次从存在的数字中取出最小的数,然后找到最大的连续数字后,减少取出的数字次数,并使答案+1。 重复上述操作至不存在数字。
代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#define x first
#define y second
using namespace std;
typedef long long LL;
const int N =1e5+10 , mod = 1e9 +7 ;
int x ;
int main()
{
int T ; cin >>T;
while( T --){
map<int,int> mp;
set<int> s;
int n ;cin >> n ;
for(int i = 0 ; i < n; ++i){
int x ;cin >>x ;
if( !mp[x]) s.insert( x ) ;
mp[x] ++ ;
}
int ans = 0 ;
while( s.size()) {
int t = *s.begin() ;
for(int i = t; mp[i] ; ++i){
mp[i]--;
if( !mp[i]) s.erase( i) ;
}
++ans ;
}
cout<< ans << endl;
}
return 0 ;
}
问题四:1789C - Serval and Toxel's Arrays
大意:有一个长度为n的每个元素都不同的数组a,我将给出m个指令,每个指令有一个pi 和x, 意指将a[ pi]的元素改为x。 设初始数组为A0, 操作1次后的数组为A1, 操作i次的为Ai ... 操作m次后为Am。若将Ai和Aj并在一起(i<j),存在的不同元素的个数记为r(i,j) ,求两两数组之间不同元素的个数之和。操作保证,每次操作后的数组中,每个元素都不同。
1<= ai <=n + m
思路:显然,若每次修改的x都在前面的数组中没有出现过,那结果为 n*m*(m+1).若某个元素总共出现了x次,那么就多算了 x*(x-1)/2次 , 则需要从结果中减去。
代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#define x first
#define y second
using namespace std;
typedef long long LL;
const int N =4e5+10 , mod = 1e9 +7 ;
int a[N ] ;
LL cnt[N] ;
bool st[N] ;
int main()
{
int T ; cin >>T;
while( T --){
LL n , m ;
cin >> n >> m ;
memset( cnt, 0 , sizeof cnt) ;
for(int i = 1 ; i<=n; ++i) cin >>a[ i ] ;
for(int i =1;i <=n; ++i) cnt[a[i]] = m+1;
for(int i=1; i <=m ; ++i){
int pos , x ;
cin >> pos >> x;
cnt[ a[pos] ] -= m-i+1;
cnt[ x] += m-i+1;
a[pos] = x;
}
LL ans = n*m*(m+1);
for(int i =1;i <=n+m; ++i)
ans -= cnt[i]*( cnt[i]-1) /2 ;
cout<< ans <<endl;
}
return 0 ;
}
大意:给定一个n值,请你判断是否有一种2*n的排列p, 使得 p1+p2 = s1 , p3+p4 = s1+1, ...,p2n-1 + p2n = s1+ n-1。 如果有则输出YES和该排列, 否则输出NO。
思路:由等差数列求和等性质可得,s1 = 3*( n+1)/2 。 显然,若n为偶数,则必然不存在。
若n为奇数, 不难发现对于每个i,都有( n*2 - i*2 +1) %n + n +1满足题意。(此处不会推导证明)
代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N =4e5+10 , mod = 1e9 +7 ;
int n ;
bool st[N] ;
PII res[N] ;
int main()
{
int T ; cin >>T;
while( T --){
cin >> n ;
memset( st, false ,sizeof st) ;
if( (n&1) == 0) {
cout<< "No" <<endl;continue;
}else cout<<"Yes"<<endl;
for(int i =1; i <=n; ++i){
cout<<i <<" " << ( n*2 - i*2 +1) %n + n +1 <<endl;
}
}
return 0 ;
}
问题六:1786B - Cake Assembly Line
大意:给定n长度数组a,b。再给定w和h。现在能使ai的每个元素都增加或减少任意值,问是否存在某个值,对a的每个元素进行操作后,有 [bi-h,bi+h] 包含于 [ai-w, ai+w]。
题目保证: ai+w < ai+1 - w
思路:由题意得, 每个ai都对应一个bi。我们可以先是将a1的左区间与b1的左区间对准,以增加为正方向,确定可移动的范围[L,R]。之后每考虑ai,则考虑ai,bi的移动范围[L1,R1],更新L = man(L,L1) , R= ( R , R1) 。若L>R ,则说明不存在某个移动范围,能使得所有ai的区间都符合题意。
代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N =4e5+10 , mod = 1e9 +7 ;
int n ,w,h;
int a[N],b[N] ;
int main()
{
int T ; cin >>T;
while( T --){
cin >> n >>w >> h ;
for(int i = 0 ; i < n ; ++i) cin >>a[ i ] ;
for(int i = 0 ;i < n ; ++i) cin >>b[i] ;
int add = a[0]-w - ( b[0]-h) ;
for(int i = 0 ; i < n ; ++i) a[i] -= add ;
bool flag= true;
int l = -1e9 , r = 1e9 ;
for(int i = 0 ; i < n ; ++i){
int ll = b[i] -h , rr = b[i] +h ;
int L = a[i] -w , R =a[i] +w ;
l = max( l , L-ll);
r = min( r, R-rr) ;
if( l > r ) break;
}
if( l > r ) cout<<"NO" <<endl;
else cout<<"YES" <<endl;
}
return 0 ;
}